Skip to content
Draft
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
1 change: 1 addition & 0 deletions ddtrace/internal/datastreams/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ def periodic(self) -> None:
raw_payload["Env"] = compat.ensure_text(config.env)
if config.version:
raw_payload["Version"] = compat.ensure_text(config.version)
process_tags._set_globals()
if p_tags := process_tags.process_tags_list:
raw_payload["ProcessTags"] = p_tags

Expand Down
15 changes: 14 additions & 1 deletion ddtrace/internal/process_tags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
ENTRYPOINT_TYPE_TAG = "entrypoint.type"
ENTRYPOINT_TYPE_SCRIPT = "script"
ENTRYPOINT_BASEDIR_TAG = "entrypoint.basedir"
SVC_USER_TAG = "svc.user"
SVC_AUTO_TAG = "svc.auto"

_CONSECUTIVE_UNDERSCORES_PATTERN = re.compile(r"_{2,}")
_ALLOWED_CHARS = _ALLOWED_CHARS = frozenset("abcdefghijklmnopqrstuvwxyz0123456789/._-")
Expand Down Expand Up @@ -62,11 +64,15 @@ def generate_process_tags() -> tuple[Optional[str], Optional[list[str]]]:
if not config.enabled:
return None, None

from ddtrace import config as ddtrace_config

Choose a reason for hiding this comment

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

P1 Badge Avoid importing ddtrace.config during process-tags init

When DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=true, generate_process_tags() executes at module import time and hits from ddtrace import config; at that point ddtrace/__init__.py has imported telemetry (which imports process_tags) before it binds config, so this creates a circular import and can raise ImportError: cannot import name 'config' from partially initialized module 'ddtrace', preventing startup whenever process tags are enabled from env.

Useful? React with 👍 / 👎.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think this is true. The test I added proves it.

Copy link
Contributor

Choose a reason for hiding this comment

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

So we have a problem because, tests are passing but codex is right, if you run
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=True ddtrace-run python your_script.py, you indeed have a circular import (that is also why I created a config for process_tags).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah yes. This arises from the fact that process tags are computed before/during ddtrace configs and they also need to include information from those configs.


tag_definitions = [
(ENTRYPOINT_WORKDIR_TAG, lambda: os.path.basename(os.getcwd())),
(ENTRYPOINT_BASEDIR_TAG, lambda: Path(sys.argv[0]).resolve().parent.name),
(ENTRYPOINT_NAME_TAG, lambda: os.path.splitext(os.path.basename(sys.argv[0]))[0]),
(ENTRYPOINT_TYPE_TAG, lambda: ENTRYPOINT_TYPE_SCRIPT),
(SVC_USER_TAG, lambda: "true" if ddtrace_config._is_user_provided_service else None),
(SVC_AUTO_TAG, lambda: ddtrace_config.service if not ddtrace_config._is_user_provided_service else None),
]

process_tags_list = sorted(
Expand All @@ -93,4 +99,11 @@ def compute_base_hash(container_tags_hash):


base_hash, base_hash_bytes = None, b""
process_tags, process_tags_list = generate_process_tags()
process_tags = None


def _set_globals():
global process_tags
global process_tags_list
if not process_tags:
process_tags, process_tags_list = generate_process_tags()
1 change: 1 addition & 0 deletions ddtrace/internal/remoteconfig/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def __init__(self) -> None:
tags=[":".join(_) for _ in tags.items()],
)

process_tags._set_globals()
if p_tags_list := process_tags.process_tags_list:
self._client_tracer["process_tags"] = p_tags_list

Expand Down
1 change: 1 addition & 0 deletions ddtrace/internal/runtime/tag_collectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,6 @@ class ProcessTagCollector(RuntimeTagCollector):
def collect_fn(self, keys):
# DEV: we do not access direct process_tags_list so we can
# reload it in the tests
process_tags._set_globals()
process_tags_list = process_tags.process_tags_list
return process_tags_list or []
2 changes: 2 additions & 0 deletions ddtrace/internal/telemetry/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def _get_application(key: tuple[str, str, str]) -> dict:
"runtime_version": _format_version_info(sys.implementation.version),
}

process_tags._set_globals()

if p_tags := process_tags.process_tags:
application["process_tags"] = p_tags

Expand Down
11 changes: 11 additions & 0 deletions tests/internal/test_process_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,17 @@ def test_process_tags_activated(self):
with self.tracer.trace("child"):
pass

@pytest.mark.snapshot
@run_in_subprocess(env_overrides=dict(DD_SERVICE="foobar"))
def test_process_tags_user_defined_service(self):
with patch("sys.argv", [TEST_SCRIPT_PATH]), patch("os.getcwd", return_value=TEST_WORKDIR_PATH):
config.enabled = True # type: ignore[assignment]
process_tag_reload()

with self.tracer.trace("parent"):
with self.tracer.trace("child"):
pass

@pytest.mark.snapshot
def test_process_tags_edge_case(self):
with patch("sys.argv", ["/test_script"]), patch("os.getcwd", return_value=TEST_WORKDIR_PATH):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"meta": {
"_dd.p.dm": "-0",
"_dd.p.tid": "6911da3a00000000",
"_dd.tags.process": "entrypoint.basedir:,entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir",
"_dd.tags.process": "entrypoint.basedir:,entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir,svc.auto:tests.internal",
"language": "python",
"runtime-id": "c9342b8003de45feb0bf56d32ece46a1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"meta": {
"_dd.p.dm": "-0",
"_dd.p.tid": "6911dc5a00000000",
"_dd.tags.process": "entrypoint.basedir:to,entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir",
"_dd.tags.process": "entrypoint.basedir:to,entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir,svc.auto:tests.internal",
"language": "python",
"runtime-id": "2d5de91f8dd9442cad7faca5554a09f1"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"meta": {
"_dd.p.dm": "-0",
"_dd.p.tid": "69244c0600000000",
"_dd.tags.process": "entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir",
"_dd.tags.process": "entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir,svc.auto:tests.internal",
"language": "python",
"runtime-id": "9f7b2a86304f4a82b0ad44b603ed29a0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"meta": {
"_dd.p.dm": "-0",
"_dd.p.tid": "6924399800000000",
"_dd.tags.process": "entrypoint.type:script,entrypoint.workdir:workdir",
"_dd.tags.process": "entrypoint.type:script,entrypoint.workdir:workdir,svc.auto:tests.internal",
"language": "python",
"runtime-id": "917df14013db49fdaeea24fe2021f42a"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[[
{
"name": "parent",
"service": "foobar",
"resource": "parent",
"trace_id": 0,
"span_id": 1,
"parent_id": 0,
"type": "",
"error": 0,
"meta": {
"_dd.p.dm": "-0",
"_dd.p.tid": "6997861b00000000",
"_dd.tags.process": "entrypoint.basedir:to,entrypoint.name:test_script,entrypoint.type:script,entrypoint.workdir:workdir,svc.user:true",
"language": "python",
"runtime-id": "9b3d6828daea467087a29c4c44faa317"
},
"metrics": {
"_dd.top_level": 1.0,
"_dd.tracer_kr": 1.0,
"_sampling_priority_v1": 1.0,
"process_id": 54689.0
},
"duration": 952166,
"start": 1771537947768385679
},
{
"name": "child",
"service": "foobar",
"resource": "child",
"trace_id": 0,
"span_id": 2,
"parent_id": 1,
"type": "",
"error": 0,
"duration": 61917,
"start": 1771537947769090137
}]]
Loading