From d84c1e60bf31058501ed840ccdae119d61682971 Mon Sep 17 00:00:00 2001 From: RichardoMrMu <44485717+RichardoMrMu@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:45:25 +0800 Subject: [PATCH 1/3] fix(prometheus): tolerate invalid OTEL_EXPORTER_PROMETHEUS_PORT env var _AutoPrometheusMetricReader parsed OTEL_EXPORTER_PROMETHEUS_PORT with a bare int() in __init__, so a non-integer value (e.g. "9464abc") or an empty string raised ValueError while building the auto-configured reader and took opentelemetry-instrument/SDK startup down. This is the same class of startup crash already fixed for the OTLP exporters (HTTP _resolve_timeout in #5389, gRPC _timeout_from_env in #5448) and the Zipkin exporters (#5704). Add a private _port_from_env() that skips unset/empty values silently, logs a warning for non-integer values, and falls back to the 9464 default. Explicit configuration is unaffected (the reader's auto server only reads the env var). Assisted-by: Doubao --- .../exporter/prometheus/__init__.py | 28 ++++++++++++++++++- .../tests/test_entrypoints.py | 22 +++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py index 0de39be9893..bcf7bafad79 100644 --- a/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py +++ b/exporter/opentelemetry-exporter-prometheus/src/opentelemetry/exporter/prometheus/__init__.py @@ -112,6 +112,32 @@ _TARGET_INFO_NAME = "target" _TARGET_INFO_DESCRIPTION = "Target metadata" +_DEFAULT_PORT = 9464 + + +def _port_from_env() -> int: + """Return ``OTEL_EXPORTER_PROMETHEUS_PORT`` as an int, defaulting to + ``_DEFAULT_PORT`` when the variable is unset, empty, or not an integer. + + An invalid value is logged and ignored instead of raising, mirroring the + OTLP/Zipkin exporters so a malformed environment variable cannot take + auto-instrumentation startup down. + """ + raw = environ.get(OTEL_EXPORTER_PROMETHEUS_PORT) + if raw is None or not raw.strip(): + return _DEFAULT_PORT + try: + return int(raw) + except ValueError: + _logger.warning( + "Invalid value %r for %s, using default port %s", + raw, + OTEL_EXPORTER_PROMETHEUS_PORT, + _DEFAULT_PORT, + ) + return _DEFAULT_PORT + + _OTEL_SCOPE_NAME_LABEL = "otel_scope_name" _OTEL_SCOPE_VERSION_LABEL = "otel_scope_version" _OTEL_SCOPE_SCHEMA_URL_LABEL = "otel_scope_schema_url" @@ -510,6 +536,6 @@ def __init__(self) -> None: # Default values are specified in # https://github.com/open-telemetry/opentelemetry-specification/blob/v1.24.0/specification/configuration/sdk-environment-variables.md#prometheus-exporter start_http_server( - port=int(environ.get(OTEL_EXPORTER_PROMETHEUS_PORT, "9464")), + port=_port_from_env(), addr=environ.get(OTEL_EXPORTER_PROMETHEUS_HOST, "localhost"), ) diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py index 9eb7e037e3b..c53308240b5 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py @@ -52,3 +52,25 @@ def test_starts_http_server_host_envvar(self, mock_start_http_server: Mock) -> N def test_starts_http_server_port_envvar(self, mock_start_http_server: Mock) -> None: _AutoPrometheusMetricReader() mock_start_http_server.assert_called_once_with(port=9999, addr=ANY) + + @patch("opentelemetry.exporter.prometheus.start_http_server") + @patch.dict(os.environ, {OTEL_EXPORTER_PROMETHEUS_PORT: "9464abc"}, clear=False) + def test_starts_http_server_invalid_port_envvar_falls_back( + self, mock_start_http_server: Mock + ) -> None: + with self.assertLogs("opentelemetry.exporter.prometheus", level="WARNING") as cm: + _AutoPrometheusMetricReader() + + mock_start_http_server.assert_called_once_with(port=9464, addr=ANY) + self.assertTrue( + any("Invalid value" in message for message in cm.output), + cm.output, + ) + + @patch("opentelemetry.exporter.prometheus.start_http_server") + @patch.dict(os.environ, {OTEL_EXPORTER_PROMETHEUS_PORT: ""}, clear=False) + def test_starts_http_server_empty_port_envvar_falls_back( + self, mock_start_http_server: Mock + ) -> None: + _AutoPrometheusMetricReader() + mock_start_http_server.assert_called_once_with(port=9464, addr=ANY) From d82e0eae0a892a2af610558c2e6ca3d3f50eaea4 Mon Sep 17 00:00:00 2001 From: RichardoMrMu <44485717+RichardoMrMu@users.noreply.github.com> Date: Tue, 29 Sep 2026 01:47:32 +0800 Subject: [PATCH 2/3] changelog: add fragment for PR #5705 --- .changelog/5705.fixed | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/5705.fixed diff --git a/.changelog/5705.fixed b/.changelog/5705.fixed new file mode 100644 index 00000000000..6165ab0a5b8 --- /dev/null +++ b/.changelog/5705.fixed @@ -0,0 +1 @@ +`opentelemetry-exporter-prometheus`: warn and fall back to the default port instead of raising `ValueError` when `OTEL_EXPORTER_PROMETHEUS_PORT` holds a non-integer value in the auto-configured metric reader. From 1d83974cd564c7595eff8146d5554d1802dfa689 Mon Sep 17 00:00:00 2001 From: RichardoMrMu <947676438@qq.com> Date: Mon, 12 Oct 2026 13:02:11 +0800 Subject: [PATCH 3/3] test(prometheus): ruff-format the new entrypoint timeout tests The `misc / precommit` job failed because the two added test signatures were split across lines while ruff (v0.16.0, line-length 120) keeps them on one line. Run `ruff format` so `pre-commit run ruff-format` is clean. --- .../tests/test_entrypoints.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py index c53308240b5..af00a4dedb1 100644 --- a/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py +++ b/exporter/opentelemetry-exporter-prometheus/tests/test_entrypoints.py @@ -55,9 +55,7 @@ def test_starts_http_server_port_envvar(self, mock_start_http_server: Mock) -> N @patch("opentelemetry.exporter.prometheus.start_http_server") @patch.dict(os.environ, {OTEL_EXPORTER_PROMETHEUS_PORT: "9464abc"}, clear=False) - def test_starts_http_server_invalid_port_envvar_falls_back( - self, mock_start_http_server: Mock - ) -> None: + def test_starts_http_server_invalid_port_envvar_falls_back(self, mock_start_http_server: Mock) -> None: with self.assertLogs("opentelemetry.exporter.prometheus", level="WARNING") as cm: _AutoPrometheusMetricReader() @@ -69,8 +67,6 @@ def test_starts_http_server_invalid_port_envvar_falls_back( @patch("opentelemetry.exporter.prometheus.start_http_server") @patch.dict(os.environ, {OTEL_EXPORTER_PROMETHEUS_PORT: ""}, clear=False) - def test_starts_http_server_empty_port_envvar_falls_back( - self, mock_start_http_server: Mock - ) -> None: + def test_starts_http_server_empty_port_envvar_falls_back(self, mock_start_http_server: Mock) -> None: _AutoPrometheusMetricReader() mock_start_http_server.assert_called_once_with(port=9464, addr=ANY)