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.
Describe your environment
OS: Ubuntu 24.04
Python version: 3.12.3
SDK version: 1.45.0.dev, from
mainat5aa2f8fAPI version: same
grpcio 1.83.1
What happened?
With
OTEL_PYTHON_SDK_INTERNAL_METRICS_ENABLED=true, the internal exporter metrics carry aserver.portthe exporter never connects to, and for a scheme-less endpoint they carry no
server.addressorserver.portat all._exporter_metrics.py:77-82fills in an HTTP default when the endpoint omits the port: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.comthe attribute reads 80 while the channel dials 443. The OTLP/gRPC default in thespecification is 4317, which is neither.
Separately,
otlp.example.com:4317with no scheme is an endpoint form this exporter accepts and passes togRPC verbatim:
exporter.py:299leavesself._endpointalone whenurlparseyields an emptynetloc.It builds a secure channel, since
_insecureisFalsewithout anhttpscheme, so reaching a plaintextcollector this way needs
insecure=TrueorOTEL_EXPORTER_OTLP_INSECURE=true. The specification blessesthat combination for OTLP/gRPC. Either way the endpoint connects, and yet
urlparsegivesscheme='otlp.example.com',netloc='',path='4317', soendpoint.hostnameandendpoint.portareboth
Noneand the two attributes are dropped from every internal metric.Steps to Reproduce
For the port gRPC actually dials, with a listener bound to 127.0.0.1:443:
Expected Result
server.portmatches the port the channel connects to.server.addressandserver.portare present forevery endpoint form the exporter accepts, including a scheme-less
host:port.Actual Result
The listener on 127.0.0.1:443 accepts the connection from
insecure_channel("localhost"), so an insecurechannel with no port dials 443 and not 80:
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 internalSDK metrics, and needs no change to connection behaviour.
I did not check the HTTP exporter, which shares
_exporter_metrics.pyand where the 80 and 443 defaultsare correct.