Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4907](https://github.com/open-telemetry/opentelemetry-python/issues/4907))
- Drop Python 3.9 support
([#5076](https://github.com/open-telemetry/opentelemetry-python/pull/5076))
- `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 <module>` 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)
Expand Down
13 changes: 10 additions & 3 deletions opentelemetry-sdk/src/opentelemetry/sdk/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <module>``
# invocations where sys.argv is rewritten to the resolved module path
# and the ``-m <module>`` information is lost. sys.orig_argv also
# aligns with /proc/<pid>/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,
Expand Down
35 changes: 32 additions & 3 deletions opentelemetry-sdk/tests/resources/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,10 @@ def test_service_name_env_precedence(self):
"sys.argv",
["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],
)
@patch(
"sys.orig_argv",
["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"],
)
def test_process_detector(self):
initial_resource = Resource({"foo": "bar"})
aggregated_resource = get_aggregated_resources(
Expand Down Expand Up @@ -607,15 +611,40 @@ 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.orig_argv),
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_ARGS],
tuple(sys.orig_argv),
)

@patch("sys.argv", ["/path/to/myapp/__main__.py"])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this mock?

@patch("sys.orig_argv", ["/usr/bin/python", "-m", "myapp"])
def test_process_detector_uses_orig_argv_for_python_m(self):
"""For ``python -m <module>`` invocations sys.argv[0] is rewritten to
the resolved module path, losing the ``-m <module>`` 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],
" ".join(sys.argv),
"/usr/bin/python -m myapp",
)
self.assertEqual(
aggregated_resource.attributes[PROCESS_COMMAND_ARGS],
tuple(sys.argv),
("/usr/bin/python", "-m", "myapp"),
)

def test_resource_detector_entry_points_default(self):
Expand Down