Skip to content

Commit 7f653bf

Browse files
committed
feat(process-tags): signal service name source via svc.user/svc.auto
Implements the PHP-tracer side of RFC "Signal Service Name Source via Process Tags". Surfaces one of two mutually-exclusive process tags so the backend can distinguish user-set vs tracer-auto-resolved service names: - svc.user:true — DD_SERVICE non-empty (env, INI, OTEL fallback, RC) - svc.auto:<name> — DD_SERVICE empty; tracer auto-resolved the default Per the RFC caveats, no conclusions are drawn from the absence of both. In ddtrace_serialize_span_to_rust_span's is_first_span block, the auto-resolved default name is read directly from the root span's property_service when its _dd.svc_src is absent (Service Override Source Attribution RFC: cleared svc_src ↔ service is the global default), avoiding a second pass through datadog_default_service_name(). Each span sees its own request's state — no FPM cross-request leak. datadog_sidecar_update_process_tags now also calls ddog_sidecar_session_set_default_service_name(transport, …): - Empty CharSlice → sidecar injects svc.user:true - Normalized default → sidecar injects svc.auto:<default> Injection happens at payload emission time in libdatadog (companion PR DataDog/libdatadog#2053), so telemetry / remote-config / runtime-info / stats payloads all see consistent svc.* tagging without baking it into the static process_tags string. - New CLI .phpt tests covering svc.user, svc.auto, OTEL fallback as user-defined, and ini_set-driven runtime mutation. - New web-SAPI test ProcessTagsWebTest::testSvcTagDoesNotLeakBetweenRequests proving the per-span design holds across FPM workers. - Existing process_tags.phpt / telemetry_process_tags.phpt updated to expect the appended svc.auto tag.
1 parent 36d5401 commit 7f653bf

12 files changed

Lines changed: 208 additions & 4 deletions

File tree

ext/sidecar.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,27 @@ void datadog_sidecar_update_process_tags(void) {
153153
}
154154

155155
ddog_sidecar_session_set_process_tags(&DATADOG_G(sidecar), process_tags);
156+
157+
zend_string *dd_service = get_DD_SERVICE();
158+
if (dd_service && ZSTR_LEN(dd_service) > 0) {
159+
datadog_ffi_try("Failed updating sidecar default service name",
160+
ddog_sidecar_session_set_default_service_name(&DATADOG_G(sidecar),
161+
DDOG_CHARSLICE_C("")));
162+
} else {
163+
zend_string *default_svc = datadog_default_service_name();
164+
if (default_svc) {
165+
const char *normalized = ddog_normalize_process_tag_value((ddog_CharSlice){
166+
.ptr = ZSTR_VAL(default_svc), .len = ZSTR_LEN(default_svc)
167+
});
168+
if (normalized) {
169+
datadog_ffi_try("Failed updating sidecar default service name",
170+
ddog_sidecar_session_set_default_service_name(&DATADOG_G(sidecar),
171+
(ddog_CharSlice){ .ptr = normalized, .len = strlen(normalized) }));
172+
ddog_free_normalized_tag_value(normalized);
173+
}
174+
zend_string_release(default_svc);
175+
}
176+
}
156177
}
157178

158179
static void datadog_sidecar_setup_thread_mode(void);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace App;
4+
5+
class SetServiceController
6+
{
7+
public function render()
8+
{
9+
ini_set('datadog.service', 'request-svc');
10+
header('Content-type: text/plain; charset=utf-8');
11+
echo 'service set';
12+
}
13+
}

tests/Frameworks/Custom/Version_Autoloaded/src/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
return function(FastRoute\RouteCollector $r) {
44
$r->addRoute('GET', '/simple', '\App\SimpleController');
5+
$r->addRoute('GET', '/set_service', '\App\SetServiceController');
56
$r->addRoute('GET', '/simple_view', '\App\SimpleViewController');
67
$r->addRoute('GET', '/error', '\App\ErrorController');
78
$r->addRoute('GET', '/telemetry', '\App\TelemetryFlushController');

tests/Integrations/Custom/Autoloaded/ProcessTagsWebTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,28 @@ public function testProcessTagsEnabledForWebSapi()
6565
);
6666
$this->assertEquals($tags['entrypoint.type'], 'script');
6767
}
68+
69+
/**
70+
* Proves the per-span svc.* design has no static-state leak between
71+
* requests served by the same FPM worker: request 1 calls
72+
* ini_set('datadog.service', ...) and must report svc.user:true; request 2
73+
* reuses the same worker but does not override the service, and must
74+
* report svc.auto:<default> (not the leaked svc.user from request 1).
75+
*/
76+
public function testSvcTagDoesNotLeakBetweenRequests()
77+
{
78+
$tracesUser = $this->tracesFromWebRequest(function () {
79+
return $this->call(new RequestSpec(__FUNCTION__ . '_user', 'GET', '/set_service', []));
80+
});
81+
$userTags = $tracesUser[0][0]['meta']['_dd.tags.process'];
82+
$this->assertStringContainsString('svc.user:true', $userTags);
83+
$this->assertStringNotContainsString('svc.auto:', $userTags);
84+
85+
$tracesAuto = $this->tracesFromWebRequest(function () {
86+
return $this->call(new RequestSpec(__FUNCTION__ . '_auto', 'GET', '/simple', []));
87+
});
88+
$autoTags = $tracesAuto[0][0]['meta']['_dd.tags.process'];
89+
$this->assertStringNotContainsString('svc.user', $autoTags);
90+
$this->assertStringContainsString('svc.auto:', $autoTags);
91+
}
6892
}

tests/ext/process_tags.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ if (isset($spans[1]['meta']['_dd.process_tags'])) {
4747
?>
4848
--EXPECTF--
4949
Process tags present in root span: YES
50-
Process tags: entrypoint.basedir:ext,entrypoint.name:process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli
50+
Process tags: entrypoint.basedir:ext,entrypoint.name:process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli,svc.auto:process_tags.php
5151
Keys sorted: YES
5252
Process tags present in child span: NO

tests/ext/svc_auto_tag_cli.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Process tags include svc.auto:<default> when DD_SERVICE is unset (CLI)
3+
--ENV--
4+
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
5+
DD_TRACE_GENERATE_ROOT_SPAN=0
6+
DD_TRACE_AUTO_FLUSH_ENABLED=0
7+
--FILE--
8+
<?php
9+
$span = \DDTrace\start_span();
10+
$span->name = 'op';
11+
\DDTrace\close_span();
12+
13+
$spans = dd_trace_serialize_closed_spans();
14+
$processTags = $spans[0]['meta']['_dd.tags.process'];
15+
16+
echo "has svc.user: " . (strpos($processTags, 'svc.user') !== false ? 'YES' : 'NO') . "\n";
17+
echo "has svc.auto: " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
18+
echo "auto value matches script: " . (strpos($processTags, 'svc.auto:svc_auto_tag_cli.php') !== false ? 'YES' : 'NO') . "\n";
19+
?>
20+
--EXPECT--
21+
has svc.user: NO
22+
has svc.auto: YES
23+
auto value matches script: YES

tests/ext/svc_auto_tag_otel.phpt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
OTEL_SERVICE_NAME counts as user-defined (emits svc.user:true)
3+
--ENV--
4+
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
5+
OTEL_SERVICE_NAME=otel-app
6+
DD_TRACE_GENERATE_ROOT_SPAN=0
7+
DD_TRACE_AUTO_FLUSH_ENABLED=0
8+
--FILE--
9+
<?php
10+
$span = \DDTrace\start_span();
11+
$span->name = 'op';
12+
\DDTrace\close_span();
13+
14+
$spans = dd_trace_serialize_closed_spans();
15+
$processTags = $spans[0]['meta']['_dd.tags.process'];
16+
17+
echo "DD_SERVICE resolved to: " . ini_get('datadog.service') . "\n";
18+
echo "has svc.user:true: " . (strpos($processTags, 'svc.user:true') !== false ? 'YES' : 'NO') . "\n";
19+
echo "has svc.auto: : " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
20+
?>
21+
--EXPECT--
22+
DD_SERVICE resolved to: otel-app
23+
has svc.user:true: YES
24+
has svc.auto: : NO

tests/ext/svc_runtime_change.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Changing datadog.service at runtime recomputes svc.user/svc.auto process tags per-span
3+
--ENV--
4+
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
5+
DD_TRACE_GENERATE_ROOT_SPAN=0
6+
DD_TRACE_AUTO_FLUSH_ENABLED=0
7+
--FILE--
8+
<?php
9+
function assert_tags($label) {
10+
$spans = dd_trace_serialize_closed_spans();
11+
$tags = $spans[0]['meta']['_dd.tags.process'];
12+
$hasUser = strpos($tags, 'svc.user:true') !== false;
13+
$hasAuto = strpos($tags, 'svc.auto:') !== false;
14+
echo "$label svc.user=" . ($hasUser ? 'YES' : 'NO') . " svc.auto=" . ($hasAuto ? 'YES' : 'NO') . "\n";
15+
}
16+
17+
$span1 = \DDTrace\start_span();
18+
$span1->name = 'before';
19+
\DDTrace\close_span();
20+
assert_tags('BEFORE ');
21+
22+
ini_set('datadog.service', 'changed-svc');
23+
$span2 = \DDTrace\start_span();
24+
$span2->name = 'after_set';
25+
\DDTrace\close_span();
26+
assert_tags('AFTER ');
27+
28+
ini_restore('datadog.service');
29+
$span3 = \DDTrace\start_span();
30+
$span3->name = 'after_restore';
31+
\DDTrace\close_span();
32+
assert_tags('REVERTED');
33+
?>
34+
--EXPECT--
35+
BEFORE svc.user=NO svc.auto=YES
36+
AFTER svc.user=YES svc.auto=NO
37+
REVERTED svc.user=NO svc.auto=YES

tests/ext/svc_user_tag.phpt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Process tags include svc.user:true when DD_SERVICE is set
3+
--ENV--
4+
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
5+
DD_SERVICE=my-app
6+
DD_TRACE_GENERATE_ROOT_SPAN=0
7+
DD_TRACE_AUTO_FLUSH_ENABLED=0
8+
--FILE--
9+
<?php
10+
$span = \DDTrace\start_span();
11+
$span->name = 'op';
12+
\DDTrace\close_span();
13+
14+
$spans = dd_trace_serialize_closed_spans();
15+
$processTags = $spans[0]['meta']['_dd.tags.process'];
16+
17+
echo "has svc.user:true: " . (strpos($processTags, 'svc.user:true') !== false ? 'YES' : 'NO') . "\n";
18+
echo "has svc.auto: : " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
19+
?>
20+
--EXPECT--
21+
has svc.user:true: YES
22+
has svc.auto: : NO

0 commit comments

Comments
 (0)