From 26d8cdfbbe25d28b947309f3089ca677f51146c4 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 25 Jul 2025 11:47:06 -0500 Subject: [PATCH 01/15] add ini value --- agent/php_newrelic.h | 13 ++++++ agent/php_nrini.c | 67 +++++++++++++++++++++++++++++ agent/scripts/newrelic.ini.template | 30 +++++++++++++ 3 files changed, 110 insertions(+) diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index 9683bad28..fb83f7d5c 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -491,6 +491,19 @@ nrinibool_t span_events_enabled; /* newrelic.span_events_enabled */ nriniuint_t span_events_max_samples_stored; /* newrelic.span_events.max_samples_stored */ + +nrinistr_t dt_remote_parent_sampled; /* newrelic.distributed_tracing.sampler.remote_parent_sampled */ +nrinistr_t + dt_remote_parent_not_sampled; /* newrelic.distributed_tracing.sampler.remote_parent_not_sampled */ +/* decoding of newrelic.distributed_tracing.sampler.remote_parent_sampled and + * newrelic.distributed_tracing.sampler.remote_parent_not_sampled. + * 0 = "default" + * 1 = "always_on" + * -1 = "always_off" + */ +int dt_sampler_parent_sampled; +int dt_sampler_parent_not_sampled; + nrinistr_t trace_observer_host; /* newrelic.infinite_tracing.trace_observer.host */ nriniuint_t diff --git a/agent/php_nrini.c b/agent/php_nrini.c index 37da5a820..add838237 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -1970,6 +1970,55 @@ static PHP_INI_MH(nr_wordpress_hooks_options_mh) { return SUCCESS; } +static PHP_INI_MH(nr_dt_sampler_remote_parent_mh) { + nrinistr_t* p; + + char* base = (char*)mh_arg2; + p = (nrinistr_t*)(base + (size_t)mh_arg1); + bool parent_sampled = false; + + (void)mh_arg3; + NR_UNUSED_TSRMLS; + + p->where = 0; + + if (0 == nr_strcmp(ZEND_STRING_VALUE(entry->name), + "newrelic.distributed_tracing.sampler.remote_parent_sampled")) { + parent_sampled = true; + } + + if (0 == nr_strcmp(NEW_VALUE, "default")) { + if (parent_sampled) { + NRPRG(dt_sampler_parent_sampled) = 0; + } else { + NRPRG(dt_sampler_parent_not_sampled) = 0; + } + } else if (0 == nr_strcmp(NEW_VALUE, "always_on")) { + if (parent_sampled) { + NRPRG(dt_sampler_parent_sampled) = 1; + } else { + NRPRG(dt_sampler_parent_not_sampled) = 1; + } + } else if (0 == nr_strcmp(NEW_VALUE, "always_off")) { + if (parent_sampled) { + NRPRG(dt_sampler_parent_sampled) = -1; + } else { + NRPRG(dt_sampler_parent_not_sampled) = -1; + } + } else { + nrl_warning(NRL_INIT, "Invalid %s value \"%s\"; using \"%s\" instead.", + ZEND_STRING_VALUE(entry->name), NEW_VALUE, + DEFAULT_WORDPRESS_HOOKS_OPTIONS); + /* This will cause PHP to call the handler again with default value */ + return FAILURE; + } + + p->where = stage; + p->value = NEW_VALUE; + + return SUCCESS; +} + /* * Now for the actual INI entry table. Please note there are two types of INI * entry specification used. @@ -2930,6 +2979,24 @@ STD_PHP_INI_ENTRY_EX("newrelic.distributed_tracing_exclude_newrelic_header", newrelic_globals, 0) + +STD_PHP_INI_ENTRY_EX("newrelic.distributed_tracing.sampler.remote_parent_sampled", + "default", + NR_PHP_REQUEST, + nr_dt_sampler_remote_parent_mh, + dt_remote_parent_sampled, + zend_newrelic_globals, + newrelic_globals, + 0) +STD_PHP_INI_ENTRY_EX("newrelic.distributed_tracing.sampler.remote_parent_not_sampled", + "default", + NR_PHP_REQUEST, + nr_dt_sampler_remote_parent_mh, + dt_remote_parent_not_sampled, + zend_newrelic_globals, + newrelic_globals, + 0) + /* * This setting is not documented and affects the length of the interally used * trace id. This INI setting should not be modified unless requested by diff --git a/agent/scripts/newrelic.ini.template b/agent/scripts/newrelic.ini.template index 4c199f00b..ce752f1d4 100644 --- a/agent/scripts/newrelic.ini.template +++ b/agent/scripts/newrelic.ini.template @@ -911,6 +911,36 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" ; ;newrelic.distributed_tracing_exclude_newrelic_header = false +; Setting: newrelic.distributed_tracing.sampler.remote_parent_sampled +; Type : string +; Scope : per-directory +; Default: "default" +; Info : This option defines how the agent should handle sampling spans when +; their parent span from an upstream entity was sampled. For example, +; setting remote_parent_sampled: always_on means the agent will sample +; anything if the upstream entity sampled the parent. +; The possible values are: +; - "default": Use New Relic's standard sampling rules. +; - "always_on": Always sample spans whose upstream parent was not sampled. +; - "always_off": Always skip sampling spans whose upstream parent was not sampled. +; +;newrelic.distributed_tracing.sampler.remote_parent_sampled = "default" + +; Setting: newrelic.distributed_tracing.sampler.remote_parent_not_sampled +; Type : string +; Scope : per-directory +; Default: "default" +; Info : This option defines how the agent should handle sampling spans when +; their parent span from an upstream entity was not sampled. For example, +; setting remote_parent_not_sampled: always_off means the agent will not +; try to sample anything if the upstream entity did not sample the parent. +; The possible values are: +; - "default": Use New Relic's standard sampling rules. +; - "always_on": Always sample spans whose upstream parent was not sampled. +; - "always_off": Always skip sampling spans whose upstream parent was not sampled. +; +;newrelic.distributed_tracing.sampler.remote_parent_sampled = "default" + ; Setting: newrelic.span_events_enabled ; Type : boolean ; Scope : per-directory From b9cc590b7e57cdcffff9bfc0997d629e759553b2 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 25 Jul 2025 11:48:24 -0500 Subject: [PATCH 02/15] add feature --- agent/php_txn.c | 4 ++++ axiom/nr_distributed_trace.c | 39 ++++++++++++++++++++++++++++++++++++ axiom/nr_distributed_trace.h | 18 +++++++++++++++++ axiom/nr_txn.c | 8 ++++++++ axiom/nr_txn.h | 12 +++++++++++ 5 files changed, 81 insertions(+) diff --git a/agent/php_txn.c b/agent/php_txn.c index 800cfb7f2..5a9e5bc21 100644 --- a/agent/php_txn.c +++ b/agent/php_txn.c @@ -942,6 +942,10 @@ nr_status_t nr_php_txn_begin(const char* appnames, = is_cli ? NRINI(tt_max_segments_cli) : NRINI(tt_max_segments_web); opts.span_queue_batch_size = NRINI(agent_span_queue_size); opts.span_queue_batch_timeout = NRINI(agent_span_queue_timeout); + opts.dt_sampler_parent_sampled + = NRPRG(dt_sampler_parent_sampled); + opts.dt_sampler_parent_not_sampled + = NRPRG(dt_sampler_parent_not_sampled); opts.logging_enabled = NRINI(logging_enabled); opts.log_decorating_enabled = NRINI(log_decorating_enabled); opts.log_forwarding_enabled = NRINI(log_forwarding_enabled); diff --git a/axiom/nr_distributed_trace.c b/axiom/nr_distributed_trace.c index 25fcd27d5..bea4cf814 100644 --- a/axiom/nr_distributed_trace.c +++ b/axiom/nr_distributed_trace.c @@ -1298,3 +1298,42 @@ char* nr_distributed_trace_create_w3c_traceparent_header(const char* trace_id, return trace_parent_header; } + +void nr_distributed_trace_handle_inbound_w3c_sampled_flag( + nr_distributed_trace_t* dt, + const nrobj_t* trace_headers, + int remote_parent_sampled, + int remote_parent_not_sampled) { + const nrobj_t* traceparent = NULL; + int sampled = 0; + nr_status_t* parse_err = 0; + if (0 != remote_parent_sampled || 0 != remote_parent_not_sampled) { + traceparent = nro_get_hash_value(trace_headers, "traceparent", parse_err); + if (nrunlikely(NULL == traceparent || NR_SUCCESS != parse_err)) { + return; + } + sampled = nro_get_hash_int(traceparent, "trace_flags", parse_err); + if (nrunlikely(NR_SUCCESS != parse_err)) { + return; + } + if (sampled) { + if (0 != remote_parent_sampled) { + if (1 == remote_parent_sampled) { + dt->sampled = true; + dt->priority = 2; + } else { + dt->sampled = false; + } + } + } else { + if (0 != remote_parent_not_sampled) { + if (-1 == remote_parent_not_sampled) { + dt->sampled = false; + } else { + dt->sampled = true; + dt->priority = 2; + } + } + } + } +} diff --git a/axiom/nr_distributed_trace.h b/axiom/nr_distributed_trace.h index f59d7ae24..76b1d9602 100644 --- a/axiom/nr_distributed_trace.h +++ b/axiom/nr_distributed_trace.h @@ -409,4 +409,22 @@ bool nr_distributed_trace_accept_inbound_w3c_payload( const char* transport_type, const char** error); +/* + * Purpose : Handle upstream w3c sampled flag according to settings + * + * Params : 1. The distributed trace object + * 2. Setting for if the upstream trace is sampled + * 3. Setting for if the upstream trace is not sampled + * + * Notes : The setting objects should follow this specification: + * 1 = always keep + * 0 = ignore upstream sampled flag + * -1 = always toss + */ +void nr_distributed_trace_handle_inbound_w3c_sampled_flag( + nr_distributed_trace_t* dt, + const nrobj_t* trace_headers, + int remote_parent_sampled, + int remote_parent_not_sampled); + #endif /* NR_DISTRIBUTED_TRACE_HDR */ diff --git a/axiom/nr_txn.c b/axiom/nr_txn.c index 60e1ea5ee..744f9e077 100644 --- a/axiom/nr_txn.c +++ b/axiom/nr_txn.c @@ -2972,6 +2972,14 @@ static bool nr_txn_accept_w3c_trace_context_headers( nr_distributed_trace_accept_inbound_w3c_payload( txn->distributed_trace, trace_headers, transport_type, &error_metrics); + /* Depending on the user's INI settings, we may or may not want to + * consider the traceparent's sampled field */ + nr_distributed_trace_handle_inbound_w3c_sampled_flag( + txn->distributed_trace, + trace_headers, + txn->options.dt_sampler_parent_sampled, + txn->options.dt_sampler_parent_not_sampled); + if (error_metrics) { nr_txn_force_single_count(txn, error_metrics); } diff --git a/axiom/nr_txn.h b/axiom/nr_txn.h index 9a29fd878..838bfe72a 100644 --- a/axiom/nr_txn.h +++ b/axiom/nr_txn.h @@ -100,6 +100,18 @@ typedef struct _nrtxnopt_t { headers in favor of only W3C trace context headers */ + int dt_sampler_parent_sampled; /* how to sample spans when non- + New Relic upstream did sample. + 1 - always keep + 0 - use default sampling + -1 - always toss + */ + int dt_sampler_parent_not_sampled; /* how to sample spans when non- + New Relic upstream didn't sample. + 1 - always keep + 0 - use default sampling + -1 - always toss + */ int span_events_enabled; /* Whether span events are enabled */ size_t span_events_max_samples_stored; /* The maximum number of span events per From 9d0f2b88f7ec0cc3bc8f0563e16d397ddb753759 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 25 Jul 2025 11:48:57 -0500 Subject: [PATCH 03/15] add tests --- axiom/tests/test_txn.c | 201 ++++++++++++++++++ .../test_force_keep_headers_not_sampled.php | 61 ++++++ .../w3c/test_force_keep_headers_sampled.php | 61 ++++++ .../test_force_toss_headers_not_sampled.php | 30 +++ .../w3c/test_force_toss_headers_sampled.php | 30 +++ 5 files changed, 383 insertions(+) create mode 100644 tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php diff --git a/axiom/tests/test_txn.c b/axiom/tests/test_txn.c index 493213206..6dbb3a28f 100644 --- a/axiom/tests/test_txn.c +++ b/axiom/tests/test_txn.c @@ -6299,6 +6299,206 @@ static void test_txn_accept_distributed_trace_payload_metrics(void) { nrm_table_destroy(&txn.unscoped_metrics); } +static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { + nrtxn_t txn = {0}; + nr_hashmap_t* headers; + bool rv = true; + nrtime_t payload_timestamp_ms = 1529445826000; + nrtime_t txn_timestamp_us = 15214458260000 * NR_TIME_DIVISOR_MS; + nrtime_t delta_timestamp_us = nr_time_duration( + (payload_timestamp_ms * NR_TIME_DIVISOR_MS), txn_timestamp_us); + + tlib_fail_if_int64_t_equal("Zero duration", 0, delta_timestamp_us); + + nr_memset(&txn, 0, sizeof(nrtxn_t)); + txn.app_connect_reply = nro_new_hash(); + txn.unscoped_metrics = nrm_table_create(0); + nro_set_hash_string(txn.app_connect_reply, "trusted_account_key", "123"); + // default value for padding can be used for this test, because other + // test (test_distributed_trace_create_trace_parent_header) already + // covers using trace_id of different lengths when w3c header is created. + txn.options.distributed_tracing_pad_trace_id = false; + txn.options.distributed_tracing_enabled = true; + +#define TEST_TXN_ACCEPT_DT_PAYLOAD_RESET \ + txn.distributed_trace->inbound.set = 0; \ + nrm_table_destroy(&txn.unscoped_metrics); \ + txn.unscoped_metrics = nrm_table_create(0); + + headers = nr_hashmap_create(NULL); + + /* + * Test : DT traceparent sampled flag INI settings + */ + // 1: upstream not sampled, agent discard + txn.options.dt_sampler_parent_not_sampled = -1; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-1-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_false("Sampled should be set to false", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + // 2: upstream not sampled, agent keep + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 1; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-0-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_true("Sampled should be set to true", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_double_equal("Priority should be set to max", 2.0, + txn.distributed_trace->priority); + + // 3: upstream not sampled, agent default (keep) + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-1-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_true("Sampled should be set to true", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + // 4: upstream not sampled, agent default (toss) + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-0-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_false("Sampled should be set to false", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + // 5: upstream sampled, agent discard + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = -1; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-1-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_false("Sampled should be set to false", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + // 6: upstream sampled, agent keep + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = 1; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-0-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_true("Sampled should be set to true", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_double_equal("Priority should be set to max", 2.0, + txn.distributed_trace->priority); + + // 7: upstream sampled, agent default (keep) + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-1-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_true("Sampled should be set to true", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + // 8: upstream sampled, agent default (toss) + TEST_TXN_ACCEPT_DT_PAYLOAD_RESET + txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_sampled = 0; + nr_distributed_trace_destroy(&txn.distributed_trace); + txn.distributed_trace = nr_distributed_trace_create(); + nr_hashmap_update(headers, NR_PSTR("traceparent"), + "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); + nr_hashmap_update(headers, NR_PSTR("tracestate"), + "123@nr=0-2-account-app-span-transaction-0-1.1273-" + "1529445826000"); + rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); + tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", + (int)rv); + tlib_pass_if_false("Sampled should be set to false", + txn.distributed_trace->sampled, "sampled flag = %d", + (int)txn.distributed_trace->sampled); + tlib_pass_if_true("Priority should not be overwritten", + 2.0 != txn.distributed_trace->priority, + "priority is 2.0"); + + nr_txn_destroy_fields(&txn); + nr_hashmap_destroy(&headers); +} + static void test_txn_accept_distributed_trace_payload_w3c(void) { nrtxn_t txn = {0}; nr_hashmap_t* headers; @@ -8912,6 +9112,7 @@ void test_main(void* p NRUNUSED) { test_create_w3c_traceparent_header(); test_create_w3c_tracestate_header(); test_txn_accept_distributed_trace_payload_w3c(); + test_txn_accept_distributed_trace_payload_w3c_sample_flags(); test_txn_accept_distributed_trace_payload_w3c_and_nr(); test_span_queue(); test_segment_record_error(); diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php new file mode 100644 index 000000000..646437c07 --- /dev/null +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php @@ -0,0 +1,61 @@ + "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-00", + 'tracestate' => "123@nr=0-0-1349956-41346604-27ddd2d8890283b4-b28be285632bbc0a-0-1.1273-1569367663277" +); + +newrelic_accept_distributed_trace_headers($payload); diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php new file mode 100644 index 000000000..9802f960a --- /dev/null +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php @@ -0,0 +1,61 @@ + "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-01", + 'tracestate' => "123@nr=0-0-1349956-41346604-27ddd2d8890283b4-b28be285632bbc0a-0-1.1273-1569367663277" +); + +newrelic_accept_distributed_trace_headers($payload); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php new file mode 100644 index 000000000..c9065772e --- /dev/null +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php @@ -0,0 +1,30 @@ + "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-00", + 'tracestate' => "123@nr=0-0-1349956-41346604-27ddd2d8890283b4-b28be285632bbc0a-1-1.1273-1569367663277" +); + +newrelic_accept_distributed_trace_headers($payload); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php new file mode 100644 index 000000000..492065e68 --- /dev/null +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php @@ -0,0 +1,30 @@ + "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-01", + 'tracestate' => "123@nr=0-0-1349956-41346604-27ddd2d8890283b4-b28be285632bbc0a-1-1.1273-1569367663277" +); + +newrelic_accept_distributed_trace_headers($payload); From 75d8e4fe39367bc539f26424ea95b5654765c4d9 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Fri, 15 Aug 2025 13:37:28 -0500 Subject: [PATCH 04/15] touchups --- axiom/tests/test_txn.c | 80 +++++++------------ .../test_force_keep_headers_not_sampled.php | 4 +- ..._keep_headers_not_sampled_non_newrelic.php | 76 ++++++++++++++++++ .../w3c/test_force_keep_headers_sampled.php | 4 +- ...orce_keep_headers_sampled_non_newrelic.php | 77 ++++++++++++++++++ .../test_force_toss_headers_not_sampled.php | 4 +- ..._toss_headers_not_sampled_non_newrelic.php | 36 +++++++++ .../w3c/test_force_toss_headers_sampled.php | 4 +- ...orce_toss_headers_sampled_non_newrelic.php | 36 +++++++++ 9 files changed, 261 insertions(+), 60 deletions(-) create mode 100644 tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php create mode 100644 tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php diff --git a/axiom/tests/test_txn.c b/axiom/tests/test_txn.c index 6dbb3a28f..b907c2211 100644 --- a/axiom/tests/test_txn.c +++ b/axiom/tests/test_txn.c @@ -6303,6 +6303,10 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { nrtxn_t txn = {0}; nr_hashmap_t* headers; bool rv = true; + char* TRACEPARENT_SAMPLED = "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"; + char* TRACEPARENT_NOT_SAMPLED = "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"; + char* TRACESTATE_SAMPLED = "123@nr=0-2-account-app-span-transaction-1-1.1273-1529445826000"; + char* TRACESTATE_NOT_SAMPLED = "123@nr=0-2-account-app-span-transaction-0-1.1273-1529445826000"; nrtime_t payload_timestamp_ms = 1529445826000; nrtime_t txn_timestamp_us = 15214458260000 * NR_TIME_DIVISOR_MS; nrtime_t delta_timestamp_us = nr_time_duration( @@ -6320,10 +6324,12 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { txn.options.distributed_tracing_pad_trace_id = false; txn.options.distributed_tracing_enabled = true; -#define TEST_TXN_ACCEPT_DT_PAYLOAD_RESET \ - txn.distributed_trace->inbound.set = 0; \ - nrm_table_destroy(&txn.unscoped_metrics); \ - txn.unscoped_metrics = nrm_table_create(0); +#define TEST_TXN_ACCEPT_DT_PAYLOAD_RESET \ + txn.distributed_trace->inbound.set = 0; \ + nrm_table_destroy(&txn.unscoped_metrics); \ + txn.unscoped_metrics = nrm_table_create(0); \ + txn.options.dt_sampler_parent_not_sampled = 0; \ + txn.options.dt_sampler_parent_sampled = 0; headers = nr_hashmap_create(NULL); @@ -6332,14 +6338,10 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { */ // 1: upstream not sampled, agent discard txn.options.dt_sampler_parent_not_sampled = -1; - txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-1-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6353,14 +6355,10 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 2: upstream not sampled, agent keep TEST_TXN_ACCEPT_DT_PAYLOAD_RESET txn.options.dt_sampler_parent_not_sampled = 1; - txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-0-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_NOT_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6373,14 +6371,10 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 3: upstream not sampled, agent default (keep) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET txn.options.dt_sampler_parent_not_sampled = 0; - txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-1-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6394,14 +6388,10 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 4: upstream not sampled, agent default (toss) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET txn.options.dt_sampler_parent_not_sampled = 0; - txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-0-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_NOT_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6414,15 +6404,11 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 5: upstream sampled, agent discard TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; txn.options.dt_sampler_parent_sampled = -1; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-1-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6435,15 +6421,11 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 6: upstream sampled, agent keep TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; txn.options.dt_sampler_parent_sampled = 1; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-0-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_NOT_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6455,15 +6437,11 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 7: upstream sampled, agent default (keep) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-1-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6476,15 +6454,11 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 8: upstream sampled, agent default (toss) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; txn.options.dt_sampler_parent_sampled = 0; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); - nr_hashmap_update(headers, NR_PSTR("traceparent"), - "00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01"); - nr_hashmap_update(headers, NR_PSTR("tracestate"), - "123@nr=0-2-account-app-span-transaction-0-1.1273-" - "1529445826000"); + nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); + nr_hashmap_update(headers, NR_PSTR("tracestate"), TRACESTATE_NOT_SAMPLED); rv = nr_txn_accept_distributed_trace_payload(&txn, headers, "HTTP"); tlib_pass_if_true("The header should be accepted", rv, "Return value = %d", (int)rv); @@ -6497,6 +6471,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { nr_txn_destroy_fields(&txn); nr_hashmap_destroy(&headers); +#undef TEST_TXN_ACCEPT_DT_PAYLOAD_RESET } static void test_txn_accept_distributed_trace_payload_w3c(void) { @@ -6980,6 +6955,7 @@ static void test_txn_accept_distributed_trace_payload_w3c(void) { nr_txn_destroy_fields(&txn); nr_hashmap_destroy(&headers); +#undef TEST_TXN_ACCEPT_DT_PAYLOAD_RESET } static void test_txn_accept_distributed_trace_payload_w3c_and_nr(void) { diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php index 646437c07..10c6be6f2 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php @@ -5,8 +5,8 @@ */ /*DESCRIPTION -Tests the Supportability metric "Supportability/DistributedTrace/AcceptPayload/Success" -when the payload is correct. +Tests that remote_parent_not_sampled = 'always_on' works. Upstream New Relic +tracestate is set to be the opposite of the desired result. */ /*INI diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php new file mode 100644 index 000000000..7d9a1939a --- /dev/null +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -0,0 +1,76 @@ + Date: Mon, 18 Aug 2025 09:48:31 -0500 Subject: [PATCH 05/15] description --- .../w3c/test_force_keep_headers_not_sampled_non_newrelic.php | 3 +-- .../w3c/test_force_keep_headers_sampled_non_newrelic.php | 3 +-- .../w3c/test_force_toss_headers_not_sampled_non_newrelic.php | 3 +-- .../w3c/test_force_toss_headers_sampled_non_newrelic.php | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index 7d9a1939a..4aa948ebb 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -5,8 +5,7 @@ */ /*DESCRIPTION -The agent should include web transaction attributes in error traces, error -events, analytic events and span events. +Tests that remote_parent_not_sampled = 'always_on' works. */ /*INI diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php index 2846a00e2..80356a50f 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php @@ -5,8 +5,7 @@ */ /*DESCRIPTION -The agent should include web transaction attributes in error traces, error -events, analytic events and span events. +Tests that remote_parent_not_sampled = 'always_on' works. */ /*INI diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php index bb9a24e79..7af0bab39 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php @@ -5,8 +5,7 @@ */ /*DESCRIPTION -The agent should include web transaction attributes in error traces, error -events, analytic events and span events. +Tests that remote_parent_not_sampled = 'always_off' works. */ /*INI diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php index 9000aa479..3a23bc7a5 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php @@ -5,8 +5,7 @@ */ /*DESCRIPTION -The agent should include web transaction attributes in error traces, error -events, analytic events and span events. +Tests that remote_parent_not_sampled = 'always_off' works. */ /*INI From 11688fc044695981a5104f375a565e8f92dde73f Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Mon, 18 Aug 2025 09:50:01 -0500 Subject: [PATCH 06/15] Update axiom/nr_distributed_trace.c Co-authored-by: Michal Nowacki --- axiom/nr_distributed_trace.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/axiom/nr_distributed_trace.c b/axiom/nr_distributed_trace.c index bea4cf814..626d4a9cb 100644 --- a/axiom/nr_distributed_trace.c +++ b/axiom/nr_distributed_trace.c @@ -1306,13 +1306,13 @@ void nr_distributed_trace_handle_inbound_w3c_sampled_flag( int remote_parent_not_sampled) { const nrobj_t* traceparent = NULL; int sampled = 0; - nr_status_t* parse_err = 0; + nr_status_t parse_err = NR_FAILURE; if (0 != remote_parent_sampled || 0 != remote_parent_not_sampled) { - traceparent = nro_get_hash_value(trace_headers, "traceparent", parse_err); + traceparent = nro_get_hash_value(trace_headers, "traceparent", &parse_err); if (nrunlikely(NULL == traceparent || NR_SUCCESS != parse_err)) { return; } - sampled = nro_get_hash_int(traceparent, "trace_flags", parse_err); + sampled = nro_get_hash_int(traceparent, "trace_flags", &parse_err); if (nrunlikely(NR_SUCCESS != parse_err)) { return; } From 5116f41e02d501ad3099e477068cad15d4068b66 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Mon, 18 Aug 2025 09:50:13 -0500 Subject: [PATCH 07/15] Update axiom/nr_distributed_trace.h Co-authored-by: Michal Nowacki --- axiom/nr_distributed_trace.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/axiom/nr_distributed_trace.h b/axiom/nr_distributed_trace.h index 76b1d9602..7a787e8fe 100644 --- a/axiom/nr_distributed_trace.h +++ b/axiom/nr_distributed_trace.h @@ -413,8 +413,9 @@ bool nr_distributed_trace_accept_inbound_w3c_payload( * Purpose : Handle upstream w3c sampled flag according to settings * * Params : 1. The distributed trace object - * 2. Setting for if the upstream trace is sampled - * 3. Setting for if the upstream trace is not sampled + * 2. W3C trace headers objet + * 3. Setting for if the upstream trace is sampled + * 4. Setting for if the upstream trace is not sampled * * Notes : The setting objects should follow this specification: * 1 = always keep From 47e97a5f948cacca0229a1c580681a0e1bc176ea Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Mon, 18 Aug 2025 09:50:28 -0500 Subject: [PATCH 08/15] Update axiom/nr_distributed_trace.c Co-authored-by: Michal Nowacki --- axiom/nr_distributed_trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/axiom/nr_distributed_trace.c b/axiom/nr_distributed_trace.c index 626d4a9cb..9f5058146 100644 --- a/axiom/nr_distributed_trace.c +++ b/axiom/nr_distributed_trace.c @@ -1316,7 +1316,7 @@ void nr_distributed_trace_handle_inbound_w3c_sampled_flag( if (nrunlikely(NR_SUCCESS != parse_err)) { return; } - if (sampled) { + if (sampled & 0x01) { if (0 != remote_parent_sampled) { if (1 == remote_parent_sampled) { dt->sampled = true; From b3b5db2cbf8723d9c15ac54e1e4e981843d84d31 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Mon, 18 Aug 2025 13:21:32 -0500 Subject: [PATCH 09/15] test headers --- ...orce_keep_headers_not_sampled_non_newrelic.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index 4aa948ebb..eeaebae59 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -71,5 +71,20 @@ ] */ +/*EXPECT_REGEX +ok - insert function succeeded +[0-9a-f]+-[0-9a-f]+-[0-9a-f]+-01 +[0-9a-f]+@nr=0-0-[0-9a-f]+-[0-9a-f]+-[0-9a-e]+-[0-9a-f]+-1-2.000000-[0-9a-f]+ +*/ + +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); + header('Content-Type: text/html'); header('Content-Length: 41'); + +$headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($headers), 'insert function succeeded'); +print($headers['traceparent']); +print("\n"); +print($headers['tracestate']); +print("\n"); From 8a11b904e978c5c2120aeb3c429928ad33cfa4d6 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Tue, 19 Aug 2025 09:46:59 -0500 Subject: [PATCH 10/15] better header tests --- ..._keep_headers_not_sampled_non_newrelic.php | 65 ++++--------------- ...orce_keep_headers_sampled_non_newrelic.php | 59 +++++------------ ..._toss_headers_not_sampled_non_newrelic.php | 18 ++++- ...orce_toss_headers_sampled_non_newrelic.php | 18 ++++- 4 files changed, 60 insertions(+), 100 deletions(-) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index eeaebae59..2d4f21a25 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -27,54 +27,11 @@ CONTENT_LENGTH=348 */ -/*EXPECT_SPAN_EVENTS -[ - "?? agent run id", - { - "reservoir_size": 10000, - "events_seen": 1 - }, - [ - [ - { - "traceId": "??", - "duration": "??", - "transactionId": "??", - "name": "WebTransaction\/Uri__FILE__", - "guid": "??", - "type": "Span", - "category": "generic", - "priority": "??", - "sampled": true, - "nr.entryPoint": true, - "parentId": "7d3efb1b173fecfa", - "transaction.name": "WebTransaction\/Uri__FILE__", - "timestamp": "??" - }, - {}, - { - "parent.transportType": "HTTP", - "response.headers.contentLength": 41, - "response.headers.contentType": "text\/html", - "http.statusCode": 200, - "response.statusCode": 200, - "httpResponseCode": "200", - "request.uri": "__FILE__", - "request.method": "POST", - "request.headers.host": "127.0.0.1", - "request.headers.contentType": "text\/html", - "request.headers.accept": "text\/plain", - "request.headers.contentLength": "??" - } - ] - ] -] -*/ - -/*EXPECT_REGEX +/*EXPECT ok - insert function succeeded -[0-9a-f]+-[0-9a-f]+-[0-9a-f]+-01 -[0-9a-f]+@nr=0-0-[0-9a-f]+-[0-9a-f]+-[0-9a-e]+-[0-9a-f]+-1-2.000000-[0-9a-f]+ +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok */ require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); @@ -82,9 +39,11 @@ header('Content-Type: text/html'); header('Content-Length: 41'); -$headers = array('Accept-Language' => 'en-US,en;q=0.5'); -tap_assert(newrelic_insert_distributed_trace_headers($headers), 'insert function succeeded'); -print($headers['traceparent']); -print("\n"); -print($headers['tracestate']); -print("\n"); +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '01', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '1', 'tracestate sampled flag ok'); +tap_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php index 80356a50f..938f04c97 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php @@ -27,50 +27,23 @@ CONTENT_LENGTH=348 */ -/*EXPECT_SPAN_EVENTS -[ - "?? agent run id", - { - "reservoir_size": 10000, - "events_seen": 1 - }, - [ - [ - { - "traceId": "??", - "duration": "??", - "transactionId": "??", - "name": "WebTransaction\/Uri__FILE__", - "guid": "??", - "type": "Span", - "category": "generic", - "priority": "??", - "sampled": true, - "nr.entryPoint": true, - "parentId": "7d3efb1b173fecfa", - "transaction.name": "WebTransaction\/Uri__FILE__", - "timestamp": "??" - }, - {}, - { - "parent.transportType": "HTTP", - "response.headers.contentLength": 41, - "response.headers.contentType": "text\/html", - "http.statusCode": 200, - "response.statusCode": 200, - "httpResponseCode": "200", - "request.uri": "__FILE__", - "request.method": "POST", - "request.headers.host": "127.0.0.1", - "request.headers.contentType": "text\/html", - "request.headers.accept": "text\/plain", - "request.headers.contentLength": "??" - } - - ] - ] -] +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok */ +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); + header('Content-Type: text/html'); header('Content-Length: 41'); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '01', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '1', 'tracestate sampled flag ok'); +tap_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php index 7af0bab39..f8900092e 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php @@ -27,9 +27,23 @@ CONTENT_LENGTH=348 */ -/*EXPECT_SPAN_EVENTS -null +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok */ +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); + header('Content-Type: text/html'); header('Content-Length: 41'); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '00', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '0', 'tracestate sampled flag ok'); +tap_not_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php index 3a23bc7a5..3fc69411a 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php @@ -27,9 +27,23 @@ CONTENT_LENGTH=348 */ -/*EXPECT_SPAN_EVENTS -null +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok */ +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); + header('Content-Type: text/html'); header('Content-Length: 41'); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '00', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '0', 'tracestate sampled flag ok'); +tap_not_equal($tracestate[7], '2.000000', 'tracestate priority ok'); From 0f6e6aac0326bf21bf473a18c7ad5b9573f1d4ed Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Tue, 19 Aug 2025 12:11:32 -0500 Subject: [PATCH 11/15] address feedback --- .../test_force_keep_headers_not_sampled.php | 19 ++++++- ..._keep_headers_not_sampled_non_newrelic.php | 51 +++++++++++++++--- .../w3c/test_force_keep_headers_sampled.php | 19 ++++++- ...orce_keep_headers_sampled_non_newrelic.php | 53 +++++++++++++++---- .../test_force_toss_headers_not_sampled.php | 19 ++++++- ..._toss_headers_not_sampled_non_newrelic.php | 13 ++--- .../w3c/test_force_toss_headers_sampled.php | 18 ++++++- ...orce_toss_headers_sampled_non_newrelic.php | 15 +++--- 8 files changed, 168 insertions(+), 39 deletions(-) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php index 10c6be6f2..073195757 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled.php @@ -7,6 +7,7 @@ /*DESCRIPTION Tests that remote_parent_not_sampled = 'always_on' works. Upstream New Relic tracestate is set to be the opposite of the desired result. +Spans should be sampled with a priority of 2.0, and downstream headers should reflect as such. */ /*INI @@ -46,12 +47,17 @@ } ] ] - ] */ +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok +*/ - +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); $payload = array( 'traceparent' => "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-00", @@ -59,3 +65,12 @@ ); newrelic_accept_distributed_trace_headers($payload); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '01', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '1', 'tracestate sampled flag ok'); +tap_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index 2d4f21a25..a39241ec1 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -6,6 +6,7 @@ /*DESCRIPTION Tests that remote_parent_not_sampled = 'always_on' works. +Spans should be sampled with a priority of 2.0, and downstream headers should reflect as such. */ /*INI @@ -14,11 +15,6 @@ */ /*HEADERS -X-Request-Start=1368811467146000 -Content-Type=text/html -Accept=text/plain -User-Agent=Mozilla/5.0 -Referer=http://user:pass@example.com/foo?q=bar#fragment traceparent=00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00 */ @@ -27,6 +23,48 @@ CONTENT_LENGTH=348 */ +/*EXPECT_SPAN_EVENTS +[ + "?? agent run id", + { + "reservoir_size": 10000, + "events_seen": 1 + }, + [ + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "WebTransaction\/Uri__FILE__", + "timestamp": "??", + "duration": "??", + "priority": 2.00000, + "sampled": true, + "nr.entryPoint": true, + "parentId": "??", + "transaction.name": "WebTransaction\/Uri__FILE__" + }, + {}, + { + "parent.transportType": "HTTP", + "response.headers.contentType": "text\/html", + "http.statusCode": 200, + "response.statusCode": 200, + "httpResponseCode": "200", + "request.uri": "__FILE__", + "request.method": "POST", + "request.headers.host": "??", + "request.headers.contentLength": "??" + + } + ] + ] +] +*/ + /*EXPECT ok - insert function succeeded ok - traceparent sampled flag ok @@ -36,9 +74,6 @@ require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); -header('Content-Type: text/html'); -header('Content-Length: 41'); - $outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); $traceparent = explode('-', $outbound_headers['traceparent']); diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php index f1efbbf46..9ca578f80 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled.php @@ -7,6 +7,7 @@ /*DESCRIPTION Tests that remote_parent_sampled = 'always_on' works. Upstream New Relic tracestate is set to be the opposite of the desired result. +Spans should be sampled with a priority of 2.0, and downstream headers should reflect as such. */ /*INI @@ -50,8 +51,14 @@ ] */ +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok +*/ - +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); $payload = array( 'traceparent' => "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-01", @@ -59,3 +66,13 @@ ); newrelic_accept_distributed_trace_headers($payload); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '01', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '1', 'tracestate sampled flag ok'); +tap_equal($tracestate[7], '2.000000', 'tracestate priority ok'); + diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php index 938f04c97..a5057abd5 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_sampled_non_newrelic.php @@ -5,7 +5,8 @@ */ /*DESCRIPTION -Tests that remote_parent_not_sampled = 'always_on' works. +Tests that remote_parent_sampled = 'always_on' works. +Spans should be sampled with a priority of 2.0, and downstream headers should reflect as such. */ /*INI @@ -14,11 +15,6 @@ */ /*HEADERS -X-Request-Start=1368811467146000 -Content-Type=text/html -Accept=text/plain -User-Agent=Mozilla/5.0 -Referer=http://user:pass@example.com/foo?q=bar#fragment traceparent=00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01 */ @@ -27,6 +23,48 @@ CONTENT_LENGTH=348 */ +/*EXPECT_SPAN_EVENTS +[ + "?? agent run id", + { + "reservoir_size": 10000, + "events_seen": 1 + }, + [ + [ + { + "category": "generic", + "type": "Span", + "guid": "??", + "traceId": "??", + "transactionId": "??", + "name": "WebTransaction\/Uri__FILE__", + "timestamp": "??", + "duration": "??", + "priority": 2.00000, + "sampled": true, + "nr.entryPoint": true, + "parentId": "??", + "transaction.name": "WebTransaction\/Uri__FILE__" + }, + {}, + { + "parent.transportType": "HTTP", + "response.headers.contentType": "text\/html", + "http.statusCode": 200, + "response.statusCode": 200, + "httpResponseCode": "200", + "request.uri": "__FILE__", + "request.method": "POST", + "request.headers.host": "??", + "request.headers.contentLength": "??" + + } + ] + ] +] +*/ + /*EXPECT ok - insert function succeeded ok - traceparent sampled flag ok @@ -36,9 +74,6 @@ require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); -header('Content-Type: text/html'); -header('Content-Length: 41'); - $outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); $traceparent = explode('-', $outbound_headers['traceparent']); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php index b5897f731..7506e964d 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled.php @@ -7,6 +7,7 @@ /*DESCRIPTION Tests that remote_parent_not_sampled = 'always_off' works. Upstream New Relic tracestate is set to be the opposite of the desired result. +Spans should not be sampled and downstream headers should indicate as such. */ /*INI @@ -17,9 +18,16 @@ /*EXPECT_SPAN_EVENTS null -*/ + */ +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok +*/ +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); $payload = array( @@ -28,3 +36,12 @@ ); newrelic_accept_distributed_trace_headers($payload); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '00', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '0', 'tracestate sampled flag ok'); +tap_not_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php index f8900092e..5f591c647 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_not_sampled_non_newrelic.php @@ -6,6 +6,7 @@ /*DESCRIPTION Tests that remote_parent_not_sampled = 'always_off' works. +Spans should not be sampled and downstream headers should indicate as such. */ /*INI @@ -14,11 +15,6 @@ */ /*HEADERS -X-Request-Start=1368811467146000 -Content-Type=text/html -Accept=text/plain -User-Agent=Mozilla/5.0 -Referer=http://user:pass@example.com/foo?q=bar#fragment traceparent=00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-00 */ @@ -27,6 +23,10 @@ CONTENT_LENGTH=348 */ +/*EXPECT_SPAN_EVENTS +null +*/ + /*EXPECT ok - insert function succeeded ok - traceparent sampled flag ok @@ -36,9 +36,6 @@ require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); -header('Content-Type: text/html'); -header('Content-Length: 41'); - $outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); $traceparent = explode('-', $outbound_headers['traceparent']); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php index 263471a22..e77487fe5 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled.php @@ -7,6 +7,7 @@ /*DESCRIPTION Tests that remote_parent_sampled = 'always_off' works. Upstream New Relic tracestate is set to be the opposite of the desired result. +Spans should not be sampled and downstream headers should indicate as such. */ /*INI @@ -19,8 +20,14 @@ null */ +/*EXPECT +ok - insert function succeeded +ok - traceparent sampled flag ok +ok - tracestate sampled flag ok +ok - tracestate priority ok +*/ - +require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); $payload = array( 'traceparent' => "00-74be672b84ddc4e4b28be285632bbc0a-27ddd2d8890283b4-01", @@ -28,3 +35,12 @@ ); newrelic_accept_distributed_trace_headers($payload); + +$outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); +tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); +$traceparent = explode('-', $outbound_headers['traceparent']); +$tracestate = explode('-', explode('=', $outbound_headers['tracestate'])[1]); + +tap_equal($traceparent[3], '00', 'traceparent sampled flag ok'); +tap_equal($tracestate[6], '0', 'tracestate sampled flag ok'); +tap_not_equal($tracestate[7], '2.000000', 'tracestate priority ok'); diff --git a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php index 3fc69411a..eaf95b690 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_toss_headers_sampled_non_newrelic.php @@ -5,7 +5,8 @@ */ /*DESCRIPTION -Tests that remote_parent_not_sampled = 'always_off' works. +Tests that remote_parent_sampled = 'always_off' works. +Spans should not be sampled and downstream headers should indicate as such. */ /*INI @@ -14,11 +15,6 @@ */ /*HEADERS -X-Request-Start=1368811467146000 -Content-Type=text/html -Accept=text/plain -User-Agent=Mozilla/5.0 -Referer=http://user:pass@example.com/foo?q=bar#fragment traceparent=00-87b1c9a429205b25e5b687d890d4821f-7d3efb1b173fecfa-01 */ @@ -27,6 +23,10 @@ CONTENT_LENGTH=348 */ +/*EXPECT_SPAN_EVENTS +null +*/ + /*EXPECT ok - insert function succeeded ok - traceparent sampled flag ok @@ -36,9 +36,6 @@ require_once(realpath (dirname ( __FILE__ )) . '/../../../include/tap.php'); -header('Content-Type: text/html'); -header('Content-Length: 41'); - $outbound_headers = array('Accept-Language' => 'en-US,en;q=0.5'); tap_assert(newrelic_insert_distributed_trace_headers($outbound_headers), 'insert function succeeded'); $traceparent = explode('-', $outbound_headers['traceparent']); From b97f98d786b6e49e880f65cf3320ab1c06d32cc4 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Tue, 19 Aug 2025 14:09:40 -0500 Subject: [PATCH 12/15] Update tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php Co-authored-by: Michal Nowacki --- .../w3c/test_force_keep_headers_not_sampled_non_newrelic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index a39241ec1..d450db2e2 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -36,7 +36,7 @@ "category": "generic", "type": "Span", "guid": "??", - "traceId": "??", + "traceId": "87b1c9a429205b25e5b687d890d4821f", "transactionId": "??", "name": "WebTransaction\/Uri__FILE__", "timestamp": "??", From 6b4bad0870f2983f5978847e15eb4ac4b2295297 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Tue, 19 Aug 2025 14:09:47 -0500 Subject: [PATCH 13/15] Update tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php Co-authored-by: Michal Nowacki --- .../w3c/test_force_keep_headers_not_sampled_non_newrelic.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php index d450db2e2..f70d0ec10 100644 --- a/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php +++ b/tests/integration/distributed_tracing/w3c/test_force_keep_headers_not_sampled_non_newrelic.php @@ -44,7 +44,7 @@ "priority": 2.00000, "sampled": true, "nr.entryPoint": true, - "parentId": "??", + "parentId": "7d3efb1b173fecfa", "transaction.name": "WebTransaction\/Uri__FILE__" }, {}, From 1223b68f30bf2fd8561b7f5e551bacde99629f6f Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 3 Sep 2025 10:58:41 -0500 Subject: [PATCH 14/15] use enum --- agent/php_newrelic.h | 7 ++----- agent/php_nrini.c | 12 ++++++------ axiom/nr_distributed_trace.c | 15 ++++++++------- axiom/nr_distributed_trace.h | 19 ++++++++++++------- axiom/nr_txn.h | 26 ++++++++++++++------------ axiom/tests/test_txn.c | 20 ++++++++++---------- 6 files changed, 52 insertions(+), 47 deletions(-) diff --git a/agent/php_newrelic.h b/agent/php_newrelic.h index fb83f7d5c..b213bf091 100644 --- a/agent/php_newrelic.h +++ b/agent/php_newrelic.h @@ -497,12 +497,9 @@ nrinistr_t dt_remote_parent_not_sampled; /* newrelic.distributed_tracing.sampler.remote_parent_not_sampled */ /* decoding of newrelic.distributed_tracing.sampler.remote_parent_sampled and * newrelic.distributed_tracing.sampler.remote_parent_not_sampled. - * 0 = "default" - * 1 = "always_on" - * -1 = "always_off" */ -int dt_sampler_parent_sampled; -int dt_sampler_parent_not_sampled; +nr_upstream_parent_sampling_control_t dt_sampler_parent_sampled; +nr_upstream_parent_sampling_control_t dt_sampler_parent_not_sampled; nrinistr_t trace_observer_host; /* newrelic.infinite_tracing.trace_observer.host */ diff --git a/agent/php_nrini.c b/agent/php_nrini.c index add838237..540e9ffdb 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -1989,21 +1989,21 @@ static PHP_INI_MH(nr_dt_sampler_remote_parent_mh) { if (0 == nr_strcmp(NEW_VALUE, "default")) { if (parent_sampled) { - NRPRG(dt_sampler_parent_sampled) = 0; + NRPRG(dt_sampler_parent_sampled) = DEFAULT; } else { - NRPRG(dt_sampler_parent_not_sampled) = 0; + NRPRG(dt_sampler_parent_not_sampled) = DEFAULT; } } else if (0 == nr_strcmp(NEW_VALUE, "always_on")) { if (parent_sampled) { - NRPRG(dt_sampler_parent_sampled) = 1; + NRPRG(dt_sampler_parent_sampled) = ALWAYS_KEEP; } else { - NRPRG(dt_sampler_parent_not_sampled) = 1; + NRPRG(dt_sampler_parent_not_sampled) = ALWAYS_KEEP; } } else if (0 == nr_strcmp(NEW_VALUE, "always_off")) { if (parent_sampled) { - NRPRG(dt_sampler_parent_sampled) = -1; + NRPRG(dt_sampler_parent_sampled) = ALWAYS_DROP; } else { - NRPRG(dt_sampler_parent_not_sampled) = -1; + NRPRG(dt_sampler_parent_not_sampled) = ALWAYS_DROP; } } else { nrl_warning(NRL_INIT, "Invalid %s value \"%s\"; using \"%s\" instead.", diff --git a/axiom/nr_distributed_trace.c b/axiom/nr_distributed_trace.c index 9f5058146..9da90fb53 100644 --- a/axiom/nr_distributed_trace.c +++ b/axiom/nr_distributed_trace.c @@ -1302,12 +1302,12 @@ char* nr_distributed_trace_create_w3c_traceparent_header(const char* trace_id, void nr_distributed_trace_handle_inbound_w3c_sampled_flag( nr_distributed_trace_t* dt, const nrobj_t* trace_headers, - int remote_parent_sampled, - int remote_parent_not_sampled) { + nr_upstream_parent_sampling_control_t remote_parent_sampled, + nr_upstream_parent_sampling_control_t remote_parent_not_sampled) { const nrobj_t* traceparent = NULL; int sampled = 0; nr_status_t parse_err = NR_FAILURE; - if (0 != remote_parent_sampled || 0 != remote_parent_not_sampled) { + if (DEFAULT != remote_parent_sampled || DEFAULT != remote_parent_not_sampled) { traceparent = nro_get_hash_value(trace_headers, "traceparent", &parse_err); if (nrunlikely(NULL == traceparent || NR_SUCCESS != parse_err)) { return; @@ -1316,9 +1316,10 @@ void nr_distributed_trace_handle_inbound_w3c_sampled_flag( if (nrunlikely(NR_SUCCESS != parse_err)) { return; } + /* The final bit of the trace_flags indicates the sampling decision */ if (sampled & 0x01) { - if (0 != remote_parent_sampled) { - if (1 == remote_parent_sampled) { + if (DEFAULT != remote_parent_sampled) { + if (ALWAYS_KEEP == remote_parent_sampled) { dt->sampled = true; dt->priority = 2; } else { @@ -1326,8 +1327,8 @@ void nr_distributed_trace_handle_inbound_w3c_sampled_flag( } } } else { - if (0 != remote_parent_not_sampled) { - if (-1 == remote_parent_not_sampled) { + if (DEFAULT != remote_parent_not_sampled) { + if (ALWAYS_DROP == remote_parent_not_sampled) { dt->sampled = false; } else { dt->sampled = true; diff --git a/axiom/nr_distributed_trace.h b/axiom/nr_distributed_trace.h index 7a787e8fe..2b2e5b45e 100644 --- a/axiom/nr_distributed_trace.h +++ b/axiom/nr_distributed_trace.h @@ -60,6 +60,16 @@ static const char NR_DISTRIBUTED_TRACE_W3C_TRACECONTEXT_ACCEPT_EXCEPTION[] typedef struct _nr_distributed_trace_t nr_distributed_trace_t; typedef struct _nr_distributed_trace_payload_t nr_distributed_trace_payload_t; +/* + * Control options for how to respect other-vendor upstream sampling + * decisions. + */ +typedef enum { + DEFAULT, + ALWAYS_KEEP, + ALWAYS_DROP +} nr_upstream_parent_sampling_control_t; + /* * Purpose : Creates/allocates a new distributed tracing metadata struct * instance. It's the responsibility of the caller to @@ -416,16 +426,11 @@ bool nr_distributed_trace_accept_inbound_w3c_payload( * 2. W3C trace headers objet * 3. Setting for if the upstream trace is sampled * 4. Setting for if the upstream trace is not sampled - * - * Notes : The setting objects should follow this specification: - * 1 = always keep - * 0 = ignore upstream sampled flag - * -1 = always toss */ void nr_distributed_trace_handle_inbound_w3c_sampled_flag( nr_distributed_trace_t* dt, const nrobj_t* trace_headers, - int remote_parent_sampled, - int remote_parent_not_sampled); + nr_upstream_parent_sampling_control_t remote_parent_sampled, + nr_upstream_parent_sampling_control_t remote_parent_not_sampled); #endif /* NR_DISTRIBUTED_TRACE_HDR */ diff --git a/axiom/nr_txn.h b/axiom/nr_txn.h index 838bfe72a..1b6f352f3 100644 --- a/axiom/nr_txn.h +++ b/axiom/nr_txn.h @@ -100,18 +100,20 @@ typedef struct _nrtxnopt_t { headers in favor of only W3C trace context headers */ - int dt_sampler_parent_sampled; /* how to sample spans when non- - New Relic upstream did sample. - 1 - always keep - 0 - use default sampling - -1 - always toss - */ - int dt_sampler_parent_not_sampled; /* how to sample spans when non- - New Relic upstream didn't sample. - 1 - always keep - 0 - use default sampling - -1 - always toss - */ + nr_upstream_parent_sampling_control_t + dt_sampler_parent_sampled; /* how to sample spans when non- + New Relic upstream did sample. + 1 - always keep + 0 - use default sampling + -1 - always toss + */ + nr_upstream_parent_sampling_control_t + dt_sampler_parent_not_sampled; /* how to sample spans when non- + New Relic upstream didn't sample. + 1 - always keep + 0 - use default sampling + -1 - always toss + */ int span_events_enabled; /* Whether span events are enabled */ size_t span_events_max_samples_stored; /* The maximum number of span events per diff --git a/axiom/tests/test_txn.c b/axiom/tests/test_txn.c index b907c2211..ab2c999ff 100644 --- a/axiom/tests/test_txn.c +++ b/axiom/tests/test_txn.c @@ -6328,8 +6328,8 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { txn.distributed_trace->inbound.set = 0; \ nrm_table_destroy(&txn.unscoped_metrics); \ txn.unscoped_metrics = nrm_table_create(0); \ - txn.options.dt_sampler_parent_not_sampled = 0; \ - txn.options.dt_sampler_parent_sampled = 0; + txn.options.dt_sampler_parent_not_sampled = DEFAULT; \ + txn.options.dt_sampler_parent_sampled = DEFAULT; headers = nr_hashmap_create(NULL); @@ -6337,7 +6337,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { * Test : DT traceparent sampled flag INI settings */ // 1: upstream not sampled, agent discard - txn.options.dt_sampler_parent_not_sampled = -1; + txn.options.dt_sampler_parent_not_sampled = ALWAYS_DROP; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); @@ -6354,7 +6354,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 2: upstream not sampled, agent keep TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 1; + txn.options.dt_sampler_parent_not_sampled = ALWAYS_KEEP; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); @@ -6370,7 +6370,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 3: upstream not sampled, agent default (keep) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_not_sampled = DEFAULT; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); @@ -6387,7 +6387,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 4: upstream not sampled, agent default (toss) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_not_sampled = 0; + txn.options.dt_sampler_parent_not_sampled = DEFAULT; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_NOT_SAMPLED); @@ -6404,7 +6404,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 5: upstream sampled, agent discard TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_sampled = -1; + txn.options.dt_sampler_parent_sampled = ALWAYS_DROP; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); @@ -6421,7 +6421,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 6: upstream sampled, agent keep TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_sampled = 1; + txn.options.dt_sampler_parent_sampled = ALWAYS_KEEP; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); @@ -6437,7 +6437,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 7: upstream sampled, agent default (keep) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_sampled = 0; + txn.options.dt_sampler_parent_sampled = DEFAULT; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); @@ -6454,7 +6454,7 @@ static void test_txn_accept_distributed_trace_payload_w3c_sample_flags(void) { // 8: upstream sampled, agent default (toss) TEST_TXN_ACCEPT_DT_PAYLOAD_RESET - txn.options.dt_sampler_parent_sampled = 0; + txn.options.dt_sampler_parent_sampled = DEFAULT; nr_distributed_trace_destroy(&txn.distributed_trace); txn.distributed_trace = nr_distributed_trace_create(); nr_hashmap_update(headers, NR_PSTR("traceparent"), TRACEPARENT_SAMPLED); From b6b2639a59632b33ac8a5124232c0f82af45af33 Mon Sep 17 00:00:00 2001 From: Zach Neumann Date: Wed, 3 Sep 2025 11:00:21 -0500 Subject: [PATCH 15/15] old comment --- axiom/nr_txn.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/axiom/nr_txn.h b/axiom/nr_txn.h index 1b6f352f3..2a1f3a569 100644 --- a/axiom/nr_txn.h +++ b/axiom/nr_txn.h @@ -103,16 +103,10 @@ typedef struct _nrtxnopt_t { nr_upstream_parent_sampling_control_t dt_sampler_parent_sampled; /* how to sample spans when non- New Relic upstream did sample. - 1 - always keep - 0 - use default sampling - -1 - always toss */ nr_upstream_parent_sampling_control_t dt_sampler_parent_not_sampled; /* how to sample spans when non- New Relic upstream didn't sample. - 1 - always keep - 0 - use default sampling - -1 - always toss */ int span_events_enabled; /* Whether span events are enabled */ size_t