open-telemetry / open-telemetry/opentelemetry-python
OTLP gRPC internal metrics report a server.port the exporter does not use, and none at all for a scheme-less endpoint
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 2.6k
- Forks
- 1k
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 19
Description
Describe your environment
OS: Ubuntu 24.04
Python version: 3.12.3
SDK version: 1.45.0.dev, from main at 5aa2f8f
API version: same
grpcio 1.83.1
What happened?
With OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED=true, the internal exporter metrics carry a server.port
the exporter never connects to, and for a scheme-less endpoint they carry no server.address or
server.port at all.
_exporter_metrics.py:77-82 fills in an HTTP default when the endpoint omits the port:
port = endpoint.port
if port is None:
if endpoint.scheme == "https":
port = 443
elif endpoint.scheme == "http":
port = 80
gRPC does not use 80. A target with no port resolves to 443 whether the channel is secure or not, so for
http://otlp.example.com the attribute reads 80 while the channel dials 443. The OTLP/gRPC default in the
specification is 4317, which is neither.
Separately, otlp.example.com:4317 with no scheme is an endpoint form this exporter accepts and passes to
gRPC verbatim: exporter.py:299 leaves self._endpoint alone when urlparse yields an empty netloc.
It builds a secure channel, since _insecure is False without an http scheme, so reaching a plaintext
collector this way needs insecure=True or OTEL_EXPORTER_OTLP_INSECURE=true. The specification blesses
that combination for OTLP/gRPC. Either way the endpoint connects, and yet urlparse gives
scheme='otlp.example.com', netloc='', path='4317', so endpoint.hostname and endpoint.port are
both None and the two attributes are dropped from every internal metric.
Steps to Reproduce
import os
os.environ["OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED"] = "true"
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
for endpoint in ["http://otlp.example.com", "https://otlp.example.com",
"otlp.example.com:4317", "http://localhost:4317"]:
exporter = OTLPSpanExporter(endpoint=endpoint, insecure=True)
print(endpoint, {k: v for k, v in exporter._metrics._standard_attrs.items()
if k.startswith("server")})
For the port gRPC actually dials, with a listener bound to 127.0.0.1:443:
import grpc
grpc.channel_ready_future(grpc.insecure_channel("localhost")).result(timeout=5)
Expected Result
server.port matches the port the channel connects to. server.address and server.port are present for
every endpoint form the exporter accepts, including a scheme-less host:port.
Actual Result
http://otlp.example.com {'server.address': 'otlp.example.com', 'server.port': 80}
https://otlp.example.com {'server.address': 'otlp.example.com', 'server.port': 443}
otlp.example.com:4317 {}
http://localhost:4317 {'server.address': 'localhost', 'server.port': 4317}
The listener on 127.0.0.1:443 accepts the connection from insecure_channel("localhost"), so an insecure
channel with no port dials 443 and not 80:
listening 443
GOT CONNECTION from ('127.0.0.1', 33690)
Additional context
This came out of reproducing #3619, a different defect in the same constructor: the endpoint path dropped
without a warning. This one lives in opentelemetry-exporter-otlp-proto-common, touches only the internal
SDK metrics, and needs no change to connection behaviour.
I did not check the HTTP exporter, which shares _exporter_metrics.py and where the 80 and 443 defaults
are correct.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with _exporter_metrics.py lines 77-82 and the endpoint handling around exporter.py line 299 in OTLPSpanExporter. Compare the accepted gRPC endpoint forms in the reproduction with the internal metric attributes, then verify that server.address and server.port reflect the actual gRPC target without changing connection behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- grpc, python
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100