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
17 changes: 17 additions & 0 deletions components-rs/sidecar.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,23 @@ ddog_MaybeError ddog_sidecar_session_set_config(struct ddog_SidecarTransport **t
ddog_MaybeError ddog_sidecar_session_set_process_tags(struct ddog_SidecarTransport **transport,
const struct ddog_Vec_Tag *process_tags);

/**
* Records the tracer's auto-resolved default service name for the session
* (process-bound; sidecar emits `svc.auto:<name>` when `DD_SERVICE` is not
* currently set for the active request). Pass an empty `CharSlice` to clear.
*/
ddog_MaybeError ddog_sidecar_session_set_default_service_name(struct ddog_SidecarTransport **transport,
ddog_CharSlice default_service_name);

/**
* Records whether `DD_SERVICE` is currently set for the session (per-request
* mutable; refresh on each RINIT). When `true` the sidecar emits
* `svc.user:true`; when `false` it falls back to the previously-recorded
* `svc.auto:<name>` (if any).
*/
ddog_MaybeError ddog_sidecar_session_set_user_service_defined(struct ddog_SidecarTransport **transport,
bool is_user_defined);

/**
* Enqueues a telemetry log action to be processed internally.
* Non-blocking. Logs might be dropped if the internal queue is full.
Expand Down
31 changes: 31 additions & 0 deletions ext/sidecar.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,33 @@ void datadog_sidecar_update_process_tags(void) {
}

ddog_sidecar_session_set_process_tags(&DATADOG_G(sidecar), process_tags);

// Session-bound, process-stable: the tracer's auto-resolved default name.
zend_string *default_svc = datadog_default_service_name();
if (default_svc) {
const char *normalized = ddog_normalize_process_tag_value((ddog_CharSlice){
.ptr = ZSTR_VAL(default_svc), .len = ZSTR_LEN(default_svc)
});
if (normalized) {
datadog_ffi_try("Failed updating sidecar default service name",
ddog_sidecar_session_set_default_service_name(&DATADOG_G(sidecar),
(ddog_CharSlice){ .ptr = normalized, .len = strlen(normalized) }));
ddog_free_normalized_tag_value(normalized);
}
zend_string_release(default_svc);
}

datadog_sidecar_refresh_user_service_defined();
}

void datadog_sidecar_refresh_user_service_defined(void) {
if (!DATADOG_G(sidecar)) {
return;
}
zend_string *dd_service = get_DD_SERVICE();
bool is_defined = dd_service && ZSTR_LEN(dd_service) > 0;
datadog_ffi_try("Failed updating sidecar user-service-defined flag",
ddog_sidecar_session_set_user_service_defined(&DATADOG_G(sidecar), is_defined));
}

static void datadog_sidecar_setup_thread_mode(void);
Expand Down Expand Up @@ -799,6 +826,10 @@ void datadog_sidecar_rinit(void) {
}

ddtrace_sidecar_submit_span_data_direct_defaults(&DATADOG_G(sidecar), NULL);

if (get_global_DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED()) {
datadog_sidecar_refresh_user_service_defined();
}
}

void datadog_sidecar_rshutdown(void) {
Expand Down
1 change: 1 addition & 0 deletions ext/sidecar.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ void datadog_sidecar_handle_fork(void);
bool datadog_sidecar_should_enable(ddog_RemoteConfigFlags *flags);
void datadog_sidecar_ensure_active(void);
void datadog_sidecar_update_process_tags(void);
void datadog_sidecar_refresh_user_service_defined(void);
void datadog_sidecar_finalize(bool clear_id);
void datadog_sidecar_shutdown(void);
void datadog_force_new_instance_id(void);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace App;

class SetServiceController
{
public function render()
{
ini_set('datadog.service', 'request-svc');
header('Content-type: text/plain; charset=utf-8');
echo 'service set';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

return function(FastRoute\RouteCollector $r) {
$r->addRoute('GET', '/simple', '\App\SimpleController');
$r->addRoute('GET', '/set_service', '\App\SetServiceController');
$r->addRoute('GET', '/simple_view', '\App\SimpleViewController');
$r->addRoute('GET', '/error', '\App\ErrorController');
$r->addRoute('GET', '/telemetry', '\App\TelemetryFlushController');
Expand Down
24 changes: 24 additions & 0 deletions tests/Integrations/Custom/Autoloaded/ProcessTagsWebTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,28 @@ public function testProcessTagsEnabledForWebSapi()
);
$this->assertEquals($tags['entrypoint.type'], 'script');
}

/**
* Proves the per-span svc.* design has no static-state leak between
* requests served by the same FPM worker: request 1 calls
* ini_set('datadog.service', ...) and must report svc.user:true; request 2
* reuses the same worker but does not override the service, and must
* report svc.auto:<default> (not the leaked svc.user from request 1).
*/
public function testSvcTagDoesNotLeakBetweenRequests()
{
$tracesUser = $this->tracesFromWebRequest(function () {
return $this->call(new RequestSpec(__FUNCTION__ . '_user', 'GET', '/set_service', []));
});
$userTags = $tracesUser[0][0]['meta']['_dd.tags.process'];
$this->assertStringContainsString('svc.user:true', $userTags);
$this->assertStringNotContainsString('svc.auto:', $userTags);

$tracesAuto = $this->tracesFromWebRequest(function () {
return $this->call(new RequestSpec(__FUNCTION__ . '_auto', 'GET', '/simple', []));
});
$autoTags = $tracesAuto[0][0]['meta']['_dd.tags.process'];
$this->assertStringNotContainsString('svc.user', $autoTags);
$this->assertStringContainsString('svc.auto:', $autoTags);
}
}
2 changes: 1 addition & 1 deletion tests/ext/process_tags.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ if (isset($spans[1]['meta']['_dd.process_tags'])) {
?>
--EXPECTF--
Process tags present in root span: YES
Process tags: entrypoint.basedir:ext,entrypoint.name:process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli
Process tags: entrypoint.basedir:ext,entrypoint.name:process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli,svc.auto:process_tags.php
Keys sorted: YES
Process tags present in child span: NO
23 changes: 23 additions & 0 deletions tests/ext/svc_auto_tag_cli.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Process tags include svc.auto:<default> when DD_SERVICE is unset (CLI)
--ENV--
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_TRACE_AUTO_FLUSH_ENABLED=0
--FILE--
<?php
$span = \DDTrace\start_span();
$span->name = 'op';
\DDTrace\close_span();

$spans = dd_trace_serialize_closed_spans();
$processTags = $spans[0]['meta']['_dd.tags.process'];

echo "has svc.user: " . (strpos($processTags, 'svc.user') !== false ? 'YES' : 'NO') . "\n";
echo "has svc.auto: " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
echo "auto value matches script: " . (strpos($processTags, 'svc.auto:svc_auto_tag_cli.php') !== false ? 'YES' : 'NO') . "\n";
?>
--EXPECT--
has svc.user: NO
has svc.auto: YES
auto value matches script: YES
24 changes: 24 additions & 0 deletions tests/ext/svc_auto_tag_otel.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
OTEL_SERVICE_NAME counts as user-defined (emits svc.user:true)
--ENV--
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
OTEL_SERVICE_NAME=otel-app
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_TRACE_AUTO_FLUSH_ENABLED=0
--FILE--
<?php
$span = \DDTrace\start_span();
$span->name = 'op';
\DDTrace\close_span();

$spans = dd_trace_serialize_closed_spans();
$processTags = $spans[0]['meta']['_dd.tags.process'];

echo "DD_SERVICE resolved to: " . ini_get('datadog.service') . "\n";
echo "has svc.user:true: " . (strpos($processTags, 'svc.user:true') !== false ? 'YES' : 'NO') . "\n";
echo "has svc.auto: : " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
?>
--EXPECT--
DD_SERVICE resolved to: otel-app
has svc.user:true: YES
has svc.auto: : NO
37 changes: 37 additions & 0 deletions tests/ext/svc_runtime_change.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--TEST--
Changing datadog.service at runtime recomputes svc.user/svc.auto process tags per-span
--ENV--
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_TRACE_AUTO_FLUSH_ENABLED=0
--FILE--
<?php
function assert_tags($label) {
$spans = dd_trace_serialize_closed_spans();
$tags = $spans[0]['meta']['_dd.tags.process'];
$hasUser = strpos($tags, 'svc.user:true') !== false;
$hasAuto = strpos($tags, 'svc.auto:') !== false;
echo "$label svc.user=" . ($hasUser ? 'YES' : 'NO') . " svc.auto=" . ($hasAuto ? 'YES' : 'NO') . "\n";
}

$span1 = \DDTrace\start_span();
$span1->name = 'before';
\DDTrace\close_span();
assert_tags('BEFORE ');

ini_set('datadog.service', 'changed-svc');
$span2 = \DDTrace\start_span();
$span2->name = 'after_set';
\DDTrace\close_span();
assert_tags('AFTER ');

ini_restore('datadog.service');
$span3 = \DDTrace\start_span();
$span3->name = 'after_restore';
\DDTrace\close_span();
assert_tags('REVERTED');
?>
--EXPECT--
BEFORE svc.user=NO svc.auto=YES
AFTER svc.user=YES svc.auto=NO
REVERTED svc.user=NO svc.auto=YES
22 changes: 22 additions & 0 deletions tests/ext/svc_user_tag.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Process tags include svc.user:true when DD_SERVICE is set
--ENV--
DD_EXPERIMENTAL_PROPAGATE_PROCESS_TAGS_ENABLED=1
DD_SERVICE=my-app
DD_TRACE_GENERATE_ROOT_SPAN=0
DD_TRACE_AUTO_FLUSH_ENABLED=0
--FILE--
<?php
$span = \DDTrace\start_span();
$span->name = 'op';
\DDTrace\close_span();

$spans = dd_trace_serialize_closed_spans();
$processTags = $spans[0]['meta']['_dd.tags.process'];

echo "has svc.user:true: " . (strpos($processTags, 'svc.user:true') !== false ? 'YES' : 'NO') . "\n";
echo "has svc.auto: : " . (strpos($processTags, 'svc.auto:') !== false ? 'YES' : 'NO') . "\n";
?>
--EXPECT--
has svc.user:true: YES
has svc.auto: : NO
2 changes: 1 addition & 1 deletion tests/ext/telemetry/telemetry_process_tags.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ if ($i == 300) {
?>
--EXPECTF--
Included
string(%d) "entrypoint.basedir:telemetry,entrypoint.name:telemetry_process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli"
string(%d) "entrypoint.basedir:telemetry,entrypoint.name:telemetry_process_tags,entrypoint.type:script,entrypoint.workdir:%s,runtime.sapi:cli,svc.auto:telemetry_process_tags.php"
--CLEAN--
<?php

Expand Down
41 changes: 40 additions & 1 deletion tracer/serializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1575,7 +1575,46 @@ ddog_SpanBytes *ddtrace_serialize_span_to_rust_span(ddtrace_span_data *span, ddo
if (is_first_span) {
zend_string *process_tags = datadog_process_tags_get_serialized();
if (ZSTR_LEN(process_tags)) {
ddog_add_str_span_meta_zstr(rust_span, "_dd.tags.process", process_tags);
const char *svc_tag_appendix = NULL;
const char *normalized_default = NULL;
zend_string *dd_service = get_DD_SERVICE();

if (dd_service && ZSTR_LEN(dd_service)) {
svc_tag_appendix = ",svc.user:true";
} else {
zend_array *root_meta = ddtrace_property_array(&span->root->property_meta);
if (!zend_hash_str_exists(root_meta, ZEND_STRL("_dd.svc_src"))) {
zval root_svc;
ZVAL_UNDEF(&root_svc);
datadog_convert_to_string(&root_svc, &span->root->property_service);
if (Z_TYPE(root_svc) == IS_STRING && Z_STRLEN(root_svc) > 0) {
normalized_default = ddog_normalize_process_tag_value((ddog_CharSlice){
.ptr = Z_STRVAL(root_svc), .len = Z_STRLEN(root_svc)
});
}
zval_ptr_dtor(&root_svc);
}
}

if (svc_tag_appendix || normalized_default) {
smart_str combined = {0};
smart_str_append(&combined, process_tags);
if (svc_tag_appendix) {
smart_str_appends(&combined, svc_tag_appendix);
} else {
smart_str_appends(&combined, ",svc.auto:");
smart_str_appends(&combined, normalized_default);
}
smart_str_0(&combined);
ddog_add_str_span_meta_zstr(rust_span, "_dd.tags.process", combined.s);
smart_str_free(&combined);
} else {
ddog_add_str_span_meta_zstr(rust_span, "_dd.tags.process", process_tags);
}

if (normalized_default) {
ddog_free_normalized_tag_value(normalized_default);
}
}
}

Expand Down
Loading