diff --git a/CHANGELOG.md b/CHANGELOG.md index 60244311072..ec3cb15f512 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ([#5076](https://github.com/open-telemetry/opentelemetry-python/pull/5076)) - `opentelemetry-semantic-conventions`: use `X | Y` union annotation ([#5096](https://github.com/open-telemetry/opentelemetry-python/pull/5096)) +- `opentelemetry-sdk`: Fix `ProcessResourceDetector` to use `sys.orig_argv` so that `process.command`, `process.command_line`, and `process.command_args` reflect the original invocation for `python -m ` runs (where `sys.argv[0]` is rewritten to the module path) + ([#5083](https://github.com/open-telemetry/opentelemetry-python/pull/5083)) ## Version 1.41.0/0.62b0 (2026-04-09) diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py index a04d27e9ab1..f8d1df68211 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py @@ -380,9 +380,16 @@ def detect(self) -> "Resource": _process_pid = os.getpid() _process_executable_name = sys.executable _process_executable_path = os.path.dirname(_process_executable_name) - _process_command = sys.argv[0] - _process_command_line = " ".join(sys.argv) - _process_command_args = sys.argv + # Use sys.orig_argv, which preserves the original arguments received + # by the interpreter. This correctly captures ``python -m `` + # invocations where sys.argv is rewritten to the resolved module path + # and the ``-m `` information is lost. sys.orig_argv also + # aligns with /proc//cmdline, which the OTel semantic + # conventions reference for these attributes. + _process_argv = list(sys.orig_argv) + _process_command = _process_argv[0] if _process_argv else "" + _process_command_line = " ".join(_process_argv) + _process_command_args = _process_argv resource_info = { PROCESS_RUNTIME_DESCRIPTION: sys.version, PROCESS_RUNTIME_NAME: sys.implementation.name, diff --git a/opentelemetry-sdk/tests/resources/test_resources.py b/opentelemetry-sdk/tests/resources/test_resources.py index c083eff1460..38ec5c1a702 100644 --- a/opentelemetry-sdk/tests/resources/test_resources.py +++ b/opentelemetry-sdk/tests/resources/test_resources.py @@ -561,7 +561,7 @@ def test_service_name_env_precedence(self): ) @patch( - "sys.argv", + "sys.orig_argv", ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"], ) def test_process_detector(self): @@ -607,15 +607,39 @@ def test_process_detector(self): os.path.dirname(sys.executable), ) self.assertEqual( - aggregated_resource.attributes[PROCESS_COMMAND], sys.argv[0] + aggregated_resource.attributes[PROCESS_COMMAND], sys.orig_argv[0] ) self.assertEqual( aggregated_resource.attributes[PROCESS_COMMAND_LINE], - " ".join(sys.argv), + " ".join(sys.orig_argv), ) self.assertEqual( aggregated_resource.attributes[PROCESS_COMMAND_ARGS], - tuple(sys.argv), + tuple(sys.orig_argv), + ) + + @patch("sys.orig_argv", ["/usr/bin/python", "-m", "myapp"]) + def test_process_detector_uses_orig_argv_for_python_m(self): + """For ``python -m `` invocations sys.argv[0] is rewritten to + the resolved module path, losing the ``-m `` information. + sys.orig_argv preserves the original invocation and must be preferred. + See https://github.com/open-telemetry/opentelemetry-python/issues/4518. + """ + aggregated_resource = get_aggregated_resources( + [ProcessResourceDetector()], Resource({"foo": "bar"}) + ) + + self.assertEqual( + aggregated_resource.attributes[PROCESS_COMMAND], + "/usr/bin/python", + ) + self.assertEqual( + aggregated_resource.attributes[PROCESS_COMMAND_LINE], + "/usr/bin/python -m myapp", + ) + self.assertEqual( + aggregated_resource.attributes[PROCESS_COMMAND_ARGS], + ("/usr/bin/python", "-m", "myapp"), ) def test_resource_detector_entry_points_default(self):