From 5f7f4a4d15c3878736726027c4fe1511aa3ebf56 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 21 Nov 2023 17:13:15 +0100 Subject: [PATCH 1/5] Implement extended sampling Support for tags and resources in sampling rules Allows looking at arbitrary spans too, not only root spans Signed-off-by: Bob Weinand --- ext/compatibility.h | 9 + ext/ddtrace.c | 4 +- ext/distributed_tracing_headers.c | 21 +- ext/priority_sampling/priority_sampling.c | 281 +++++++++++++----- ext/priority_sampling/priority_sampling.h | 1 + ext/random.c | 7 + ext/random.h | 1 + ext/serializer.c | 8 +- ext/span.c | 3 +- ext/span.h | 14 + tests/Unit/Encoders/MessagePackTest.php | 7 +- tests/Unit/TracerTest.php | 13 +- .../distributed_tracestate_consumption.phpt | 1 + ...ce_inherit_b3-single-span_no_sampling.phpt | 2 +- ...tributed_trace_span_link_from_headers.phpt | 3 +- ...stributed_tracing_curl_propagate_tags.phpt | 4 +- .../ext/pcntl/pcntl_fork_reseed_span_id.phpt | 2 +- .../priority_sampling/007-rule-service.phpt | 2 +- .../008-rule-service-reject.phpt | 2 +- .../010-rule-name-service-reject.phpt | 2 +- tests/ext/priority_sampling/012-default.phpt | 2 +- .../priority_sampling/014-rule-resource.phpt | 28 ++ .../015-rule-resource-reject.phpt | 28 ++ tests/ext/priority_sampling/016-rule-tag.phpt | 28 ++ .../017-rule-tag-reject.phpt | 28 ++ .../priority_sampling/018-manual.drop.phpt | 23 ++ .../priority_sampling/019-manual.keep.phpt | 23 ++ .../priority_sampling/020-rule-nonroot.phpt | 90 ++++++ .../priority_sampling/invalid-regex-rule.phpt | 2 +- tests/ext/retrieve_128_bit_trace_id.phpt | 2 +- .../limiter_reset_flush_with_open_spans.phpt | 22 +- .../check-sample-rate.phpt | 6 +- .../limited-single-span-with-match.phpt | 2 +- .../limited-single-span.phpt | 2 +- 34 files changed, 535 insertions(+), 138 deletions(-) create mode 100644 tests/ext/priority_sampling/014-rule-resource.phpt create mode 100644 tests/ext/priority_sampling/015-rule-resource-reject.phpt create mode 100644 tests/ext/priority_sampling/016-rule-tag.phpt create mode 100644 tests/ext/priority_sampling/017-rule-tag-reject.phpt create mode 100644 tests/ext/priority_sampling/018-manual.drop.phpt create mode 100644 tests/ext/priority_sampling/019-manual.keep.phpt create mode 100644 tests/ext/priority_sampling/020-rule-nonroot.phpt diff --git a/ext/compatibility.h b/ext/compatibility.h index 1461f61601..2bfa15e49b 100644 --- a/ext/compatibility.h +++ b/ext/compatibility.h @@ -65,6 +65,15 @@ static inline zval *ddtrace_assign_variable(zval *variable_ptr, zval *value) { #define MAY_BE_ARRAY 0 #define Z_EXTRA_P(z) Z_NEXT_P(z) + +#undef zval_get_long +#define zval_get_long ddtrace_zval_get_long +static inline zend_long zval_get_long(zval *op) { + if (Z_ISUNDEF_P(op)) { + return 0; + } + return _zval_get_long(op); +} #endif #if PHP_VERSION_ID < 70200 diff --git a/ext/ddtrace.c b/ext/ddtrace.c index eff1e09017..8603523216 100644 --- a/ext/ddtrace.c +++ b/ext/ddtrace.c @@ -632,7 +632,7 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb } } } else if (zend_string_equals_literal(prop_name, "traceId")) { - span->trace_id = Z_TYPE_P(value) == IS_STRING ? ddtrace_parse_userland_trace_id(Z_STR_P(value)) : (ddtrace_trace_id){ 0 }; + span->trace_id = Z_TYPE_P(value) == IS_STRING ? ddtrace_parse_hex_trace_id(Z_STRVAL_P(value), Z_STRLEN_P(value)) : (ddtrace_trace_id){ 0 }; if (!span->trace_id.low && !span->trace_id.high) { span->trace_id = (ddtrace_trace_id) { .low = span->span_id, @@ -640,6 +640,8 @@ static zval *ddtrace_root_span_data_write(zend_object *object, zend_string *memb }; value = &span->property_id; } + } else if (zend_string_equals_literal(prop_name, "samplingPriority")) { + span->explicit_sampling_priority = zval_get_long(value) != DDTRACE_PRIORITY_SAMPLING_UNKNOWN; } #if PHP_VERSION_ID >= 70400 diff --git a/ext/distributed_tracing_headers.c b/ext/distributed_tracing_headers.c index aa9355fe47..e36cd80a1c 100644 --- a/ext/distributed_tracing_headers.c +++ b/ext/distributed_tracing_headers.c @@ -7,13 +7,6 @@ ZEND_EXTERN_MODULE_GLOBALS(ddtrace); -static ddtrace_trace_id dd_parse_b3_trace_id(char *trace_id, ssize_t trace_id_len) { - return (ddtrace_trace_id){ - .high = trace_id_len > 16 ? ddtrace_parse_hex_span_id_str(trace_id, MIN(16, trace_id_len - 16)) : 0, - .low = ddtrace_parse_hex_span_id_str(trace_id + MAX(0, trace_id_len - 16), MIN(16, trace_id_len)), - }; -} - static inline bool dd_is_hex_char(char chr) { return (chr >= '0' && chr <= '9') || (chr >= 'a' && chr <= 'f'); } @@ -99,7 +92,7 @@ static ddtrace_distributed_tracing_result ddtrace_read_distributed_tracing_ids_b ++b3_ptr; } - result.trace_id = dd_parse_b3_trace_id(b3_traceid, b3_ptr - b3_traceid); + result.trace_id = ddtrace_parse_hex_trace_id(b3_traceid, b3_ptr - b3_traceid); char *b3_spanid = ++b3_ptr; while (b3_ptr < b3_end && *b3_ptr != '-') { @@ -137,7 +130,7 @@ static ddtrace_distributed_tracing_result ddtrace_read_distributed_tracing_ids_b ddtrace_distributed_tracing_result result = dd_init_empty_result(); if (read_header(ZAI_STRL("X_B3_TRACEID"), "x-b3-traceid", &trace_id_str, data)) { - result.trace_id = dd_parse_b3_trace_id(ZSTR_VAL(trace_id_str), ZSTR_LEN(trace_id_str)); + result.trace_id = ddtrace_parse_hex_trace_id(ZSTR_VAL(trace_id_str), ZSTR_LEN(trace_id_str)); zend_string_release(trace_id_str); } @@ -482,14 +475,10 @@ void ddtrace_apply_distributed_tracing_result(ddtrace_distributed_tracing_result if (!span) { DDTRACE_G(propagated_priority_sampling) = DDTRACE_G(default_priority_sampling) = result->priority_sampling; } else { - ddtrace_set_priority_sampling_on_span(span, result->priority_sampling, DD_MECHANISM_DEFAULT); - - if (result->priority_sampling == DDTRACE_PRIORITY_SAMPLING_UNSET) { - ZVAL_UNDEF(&zv); - } else { - ZVAL_LONG(&zv, result->priority_sampling); - } + ZVAL_LONG(&zv, result->priority_sampling); ddtrace_assign_variable(&span->property_propagated_sampling_priority, &zv); + + ddtrace_set_priority_sampling_on_span(span, result->priority_sampling, DD_MECHANISM_DEFAULT); } } } diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index e91e2ef011..422194531b 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -36,17 +36,15 @@ void ddtrace_try_read_agent_rate(void) { static void dd_update_decision_maker_tag(ddtrace_root_span_data *root_span, enum dd_sampling_mechanism mechanism) { zend_array *meta = ddtrace_property_array(&root_span->property_meta); - zend_long sampling_priority = ddtrace_fetch_priority_sampling_from_span(root_span); + zend_long sampling_priority = zval_get_long(&root_span->property_sampling_priority); if (Z_TYPE(root_span->property_propagated_sampling_priority) != IS_UNDEF && zval_get_long(&root_span->property_propagated_sampling_priority) == sampling_priority) { return; } - if (sampling_priority > 0 && sampling_priority != DDTRACE_PRIORITY_SAMPLING_UNSET) { - if (!zend_hash_str_exists(meta, "_dd.p.dm", sizeof("_dd.p.dm") - 1)) { - zval dm; - ZVAL_STR(&dm, zend_strpprintf(0, "-%d", mechanism)); - zend_hash_str_add_new(meta, "_dd.p.dm", sizeof("_dd.p.dm") - 1, &dm); - } + if (sampling_priority > 0 && sampling_priority != DDTRACE_PRIORITY_SAMPLING_UNKNOWN) { + zval dm; + ZVAL_STR(&dm, zend_strpprintf(0, "-%d", mechanism)); + zend_hash_str_update(meta, "_dd.p.dm", sizeof("_dd.p.dm") - 1, &dm); } else { zend_hash_str_del(meta, "_dd.p.dm", sizeof("_dd.p.dm") - 1); } @@ -63,55 +61,159 @@ static bool dd_rule_matches(zval *pattern, zval *prop) { return zai_match_regex(Z_STR_P(pattern), Z_STR_P(prop)); } -static void dd_decide_on_sampling(ddtrace_root_span_data *span) { - int priority = DDTRACE_G(default_priority_sampling); - // manual if it's not just inherited, otherwise this value is irrelevant (as sampling priority will be default) - enum dd_sampling_mechanism mechanism = DD_MECHANISM_MANUAL; - if (priority == DDTRACE_PRIORITY_SAMPLING_UNKNOWN) { - zval *rule; - double default_sample_rate = get_DD_TRACE_SAMPLE_RATE(), sample_rate = default_sample_rate >= 0 ? default_sample_rate : 1; - bool explicit_rule = default_sample_rate >= 0; +static bool dd_check_sampling_rule(zend_array *rule, ddtrace_span_data *span) { + zval *service = &span->property_service; + + zval *rule_pattern; + if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("service")))) { + if (Z_TYPE_P(service) == IS_STRING) { + zval *mapped_service = zend_hash_find(get_DD_SERVICE_MAPPING(), Z_STR_P(service)); + if (!mapped_service) { + mapped_service = service; + } + if (!dd_rule_matches(rule_pattern, mapped_service)) { + return false; + } + } + } + if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("name")))) { + if (!dd_rule_matches(rule_pattern, &span->property_name)) { + return false; + } + } + if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("resource")))) { + if (!dd_rule_matches(rule_pattern, &span->property_resource)) { + return false; + } + } + if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("tags"))) && Z_TYPE_P(rule_pattern) == IS_ARRAY) { + zend_array *tag_rules = Z_ARR_P(rule_pattern); + zend_array *meta = ddtrace_property_array(&span->property_meta); + zend_string *tag_name; + ZEND_HASH_FOREACH_STR_KEY_VAL(tag_rules, tag_name, rule_pattern) { + if (tag_name) { + zval *value; + if (!(value = zend_hash_find(meta, tag_name))) { + return false; + } + if (!dd_rule_matches(rule_pattern, value)) { + return false; + } + } + } ZEND_HASH_FOREACH_END(); + } + + return true; +} + +// If there is one rule matching in *ANY* span, then all further rules are ignored. +// Thus we check only rules until a specific index then. +static ddtrace_rule_result dd_match_rules(ddtrace_span_data *span, bool eval_root, int skip_at) { + int index = -3; - zval *service = &span->property_service; + if (++index >= skip_at) { + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; + } + + zend_array *meta = ddtrace_property_array(&span->property_meta); + if (zend_hash_str_exists(meta, ZEND_STRL("manual.keep"))) { + return (ddtrace_rule_result){ .sampling_rate = 1, .rule = -2 }; + } + + if (++index >= skip_at) { + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; + } + if (zend_hash_str_exists(meta, ZEND_STRL("manual.drop"))) { + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = -1 }; + } + + zval *rule; + ZEND_HASH_FOREACH_VAL(get_DD_TRACE_SAMPLING_RULES(), rule) { + if (++index >= skip_at) { + break; + } + + if (Z_TYPE_P(rule) != IS_ARRAY) { + continue; + } - ZEND_HASH_FOREACH_VAL(get_DD_TRACE_SAMPLING_RULES(), rule) { - if (Z_TYPE_P(rule) != IS_ARRAY) { + if (!eval_root) { + zval *applies = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("target_span")); + if (!applies || Z_TYPE_P(applies) != IS_STRING || !zend_string_equals_literal(Z_STR_P(applies), "any")) { continue; } + } - bool rule_matches = true; + zval *sample_rate_zv; + if (dd_check_sampling_rule(Z_ARR_P(rule), span) && (sample_rate_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("sample_rate")))) { + return (ddtrace_rule_result){ .sampling_rate = zval_get_double(sample_rate_zv), .rule = index }; + } + } ZEND_HASH_FOREACH_END(); - zval *rule_pattern; - if ((rule_pattern = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("service")))) { - if (Z_TYPE_P(service) == IS_STRING) { - zval *mapped_service = zend_hash_find(get_DD_SERVICE_MAPPING(), Z_STR_P(service)); - if (!mapped_service) { - mapped_service = service; - } - rule_matches &= dd_rule_matches(rule_pattern, mapped_service); - } - } - if ((rule_pattern = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("name")))) { - rule_matches &= dd_rule_matches(rule_pattern, &span->property_name); - } + return (ddtrace_rule_result){ .sampling_rate = 0, .rule = INT32_MAX }; +} - zval *sample_rate_zv; - if (rule_matches && (sample_rate_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("sample_rate")))) { - sample_rate = zval_get_double(sample_rate_zv); - explicit_rule = true; - break; - } +void ddtrace_decide_on_closed_span_sampling(ddtrace_span_data *span) { + ddtrace_root_span_data *root = span->root; + + if (zval_get_long(&root->property_propagated_sampling_priority) > 0) { + return; + } + + ddtrace_rule_result result = dd_match_rules(span, &root->span == span && !root->parent_id, root->sampling_rule.rule); + if (result.rule != INT32_MAX) { + root->sampling_rule = result; + } +} + +static ddtrace_rule_result dd_decide_on_open_span_sampling(ddtrace_root_span_data *root) { + ddtrace_span_properties *span_props = root->stack->active; + + if (!span_props) { + return root->sampling_rule; + } + + ddtrace_rule_result result = root->sampling_rule; + do { + ddtrace_span_data *span = SPANDATA(span_props); + + ddtrace_rule_result new_result = dd_match_rules(span, &root->span == span && !root->parent_id, result.rule); + if (new_result.rule != INT32_MAX) { + result = new_result; } - ZEND_HASH_FOREACH_END(); + } while ((span_props = span_props->parent)); + + return result; +} + +// When the priority is inherited from distributed tracing, and then only when drop, *only* target_span: any rule sampling (with limiter) is applied (no agent sampling) +static void dd_decide_on_sampling(ddtrace_root_span_data *span) { + int priority; + bool is_trace_root = !span->parent_id; + // manual if it's not just inherited, otherwise this value is irrelevant (as sampling priority will be default) + enum dd_sampling_mechanism mechanism = DD_MECHANISM_MANUAL; + + ddtrace_rule_result result = dd_decide_on_open_span_sampling(span); + double sample_rate = 0; + bool explicit_rule = true; + + if (is_trace_root) { + double default_sample_rate = get_DD_TRACE_SAMPLE_RATE(); + sample_rate = default_sample_rate >= 0 ? default_sample_rate : 1; + + if (result.rule != INT32_MAX) { + sample_rate = result.sampling_rate; + } else if (default_sample_rate < 0) { + explicit_rule = false; - if (!explicit_rule) { ddtrace_try_read_agent_rate(); if (DDTRACE_G(agent_rate_by_service)) { zval *env = zend_hash_str_find(ddtrace_property_array(&span->property_meta), ZEND_STRL("env")); zval *sample_rate_zv = NULL; + zval *service = &span->property_service; if (Z_TYPE_P(service) == IS_STRING && env && Z_TYPE_P(env) == IS_STRING) { - zend_string *sample_key = zend_strpprintf(0, "service:%.*s,env:%.*s",(int) Z_STRLEN_P(service), Z_STRVAL_P(service), + zend_string *sample_key = zend_strpprintf(0, "service:%.*s,env:%.*s", (int) Z_STRLEN_P(service), Z_STRVAL_P(service), (int) Z_STRLEN_P(env), Z_STRVAL_P(env)); sample_rate_zv = zend_hash_find(DDTRACE_G(agent_rate_by_service), sample_key); zend_string_release(sample_key); @@ -125,30 +227,57 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { } } } + } else if (result.rule == INT32_MAX) { + // If we are in propagated mode, we only consider rules applying to all spans, hence default handling is not active + // But let's restore the sampling priority to reject in case it was reset to avoid invalid values + if (zval_get_long(&span->property_sampling_priority) == DDTRACE_PRIORITY_SAMPLING_UNKNOWN) { + zval priority_zv; + ZVAL_LONG(&priority_zv, PRIORITY_SAMPLING_AUTO_REJECT); + ddtrace_assign_variable(&span->property_sampling_priority, &priority_zv); + } + return; + } else { + sample_rate = result.sampling_rate; + } - bool sampling = (double)genrand64_int64() < sample_rate * (double)~0ULL; - bool limited = ddtrace_limiter_active() && (sampling && !ddtrace_limiter_allow()); + // this must be stable on re-evaluation + bool sampling = (double)span->trace_id.low < sample_rate * (double)~0ULL; + bool limited = false; + if (result.rule >= 0 && ddtrace_limiter_active() && sampling) { + if (span->trace_is_limited == DD_TRACE_LIMIT_UNCHECKED) { + span->trace_is_limited = ddtrace_limiter_allow() ? DD_TRACE_UNLIMITED : DD_TRACE_LIMITED; + } + limited = span->trace_is_limited == DD_TRACE_LIMITED; + } - zval sample_rate_zv; - ZVAL_DOUBLE(&sample_rate_zv, sample_rate); + zval sample_rate_zv; + ZVAL_DOUBLE(&sample_rate_zv, sample_rate); - if (explicit_rule) { - mechanism = DD_MECHANISM_RULE; - priority = sampling && !limited ? PRIORITY_SAMPLING_USER_KEEP : PRIORITY_SAMPLING_USER_REJECT; - zend_hash_str_update(ddtrace_property_array(&span->property_metrics), ZEND_STRL("_dd.rule_psr"), &sample_rate_zv); - } else { - mechanism = DDTRACE_G(agent_rate_by_service) ? DD_MECHANISM_AGENT_RATE : DD_MECHANISM_DEFAULT; - priority = sampling && !limited ? PRIORITY_SAMPLING_AUTO_KEEP : PRIORITY_SAMPLING_AUTO_REJECT; + zend_array *metrics = ddtrace_property_array(&span->property_metrics); + if (explicit_rule) { + // manual.keep and manual.drop count as manual + mechanism = result.rule < 0 ? DD_MECHANISM_MANUAL : DD_MECHANISM_RULE; + priority = sampling && !limited ? PRIORITY_SAMPLING_USER_KEEP : PRIORITY_SAMPLING_USER_REJECT; - zend_hash_str_update(ddtrace_property_array(&span->property_metrics), ZEND_STRL("_dd.agent_psr"), &sample_rate_zv); + if (mechanism == DD_MECHANISM_MANUAL) { + zend_hash_str_del(metrics, ZEND_STRL("_dd.rule_psr")); + } else { + zend_hash_str_update(metrics, ZEND_STRL("_dd.rule_psr"), &sample_rate_zv); } - if (limited) { - zval limit_zv; - ZVAL_DOUBLE(&limit_zv, ddtrace_limiter_rate()); - zend_hash_str_update(ddtrace_property_array(&span->property_metrics), ZEND_STRL("_dd.limit_psr"), - &limit_zv); - } + zend_hash_str_del(metrics, ZEND_STRL("_dd.agent_psr")); + } else { + mechanism = DDTRACE_G(agent_rate_by_service) ? DD_MECHANISM_AGENT_RATE : DD_MECHANISM_DEFAULT; + priority = sampling && !limited ? PRIORITY_SAMPLING_AUTO_KEEP : PRIORITY_SAMPLING_AUTO_REJECT; + + zend_hash_str_update(metrics, ZEND_STRL("_dd.agent_psr"), &sample_rate_zv); + } + + if (limited) { + zval limit_zv; + ZVAL_DOUBLE(&limit_zv, ddtrace_limiter_rate()); + zend_hash_str_update(ddtrace_property_array(&span->property_metrics), ZEND_STRL("_dd.limit_psr"), + &limit_zv); } zval priority_zv; @@ -160,9 +289,6 @@ static void dd_decide_on_sampling(ddtrace_root_span_data *span) { zend_long ddtrace_fetch_priority_sampling_from_root(void) { if (!DDTRACE_G(active_stack)->root_span) { - if (DDTRACE_G(default_priority_sampling) == DDTRACE_PRIORITY_SAMPLING_UNSET) { - return DDTRACE_PRIORITY_SAMPLING_UNKNOWN; - } return DDTRACE_G(default_priority_sampling); } @@ -170,16 +296,26 @@ zend_long ddtrace_fetch_priority_sampling_from_root(void) { } zend_long ddtrace_fetch_priority_sampling_from_span(ddtrace_root_span_data *root_span) { - if (Z_TYPE(root_span->property_sampling_priority) == IS_UNDEF) { - return DDTRACE_PRIORITY_SAMPLING_UNSET; + if (Z_TYPE(root_span->property_sampling_priority) == IS_UNDEF || Z_LVAL(root_span->property_sampling_priority) == DDTRACE_PRIORITY_SAMPLING_UNKNOWN) { + root_span->explicit_sampling_priority = false; } - int sampling_priority = zval_get_long(&root_span->property_sampling_priority); - if (sampling_priority == DDTRACE_PRIORITY_SAMPLING_UNKNOWN && DDTRACE_G(default_priority_sampling) != DDTRACE_PRIORITY_SAMPLING_UNSET) { + bool decide = !root_span->explicit_sampling_priority; + + if (decide) { + // If a decision of keep was inherited the sampling decision stays unchanged, regardless of the rules + int sampling_priority = zval_get_long(&root_span->property_sampling_priority); + if (zval_get_long(&root_span->property_propagated_sampling_priority) > 0 && + (sampling_priority == PRIORITY_SAMPLING_USER_KEEP || sampling_priority == PRIORITY_SAMPLING_AUTO_KEEP)) { + decide = false; + } + } + + if (decide) { dd_decide_on_sampling(root_span); - sampling_priority = zval_get_long(&root_span->property_sampling_priority); } - return sampling_priority; + + return zval_get_long(&root_span->property_sampling_priority); } DDTRACE_PUBLIC void ddtrace_set_priority_sampling_on_root(zend_long priority, enum dd_sampling_mechanism mechanism) { @@ -194,14 +330,11 @@ DDTRACE_PUBLIC void ddtrace_set_priority_sampling_on_root(zend_long priority, en void ddtrace_set_priority_sampling_on_span(ddtrace_root_span_data *root_span, zend_long priority, enum dd_sampling_mechanism mechanism) { zval zv; - if (priority == DDTRACE_PRIORITY_SAMPLING_UNSET) { - ZVAL_UNDEF(&zv); - } else { - ZVAL_LONG(&zv, priority); - } + ZVAL_LONG(&zv, priority); ddtrace_assign_variable(&root_span->property_sampling_priority, &zv); - if (priority != DDTRACE_PRIORITY_SAMPLING_UNKNOWN && priority != DDTRACE_PRIORITY_SAMPLING_UNSET) { + if (priority != DDTRACE_PRIORITY_SAMPLING_UNKNOWN) { dd_update_decision_maker_tag(root_span, mechanism); + root_span->explicit_sampling_priority = true; } } diff --git a/ext/priority_sampling/priority_sampling.h b/ext/priority_sampling/priority_sampling.h index 528f6c738f..5c9a18ede9 100644 --- a/ext/priority_sampling/priority_sampling.h +++ b/ext/priority_sampling/priority_sampling.h @@ -24,6 +24,7 @@ DDTRACE_PUBLIC void ddtrace_set_priority_sampling_on_root(zend_long priority, en void ddtrace_set_priority_sampling_on_span(ddtrace_root_span_data *root_span, zend_long priority, enum dd_sampling_mechanism mechanism); zend_long ddtrace_fetch_priority_sampling_from_span(ddtrace_root_span_data *root_span); zend_long ddtrace_fetch_priority_sampling_from_root(void); +void ddtrace_decide_on_closed_span_sampling(ddtrace_span_data *span); void ddtrace_try_read_agent_rate(void); diff --git a/ext/random.c b/ext/random.c index 22fc54c0e7..0ff8b5e33a 100644 --- a/ext/random.c +++ b/ext/random.c @@ -67,6 +67,13 @@ ddtrace_trace_id ddtrace_parse_userland_trace_id(zend_string *tid) { return num; } +ddtrace_trace_id ddtrace_parse_hex_trace_id(char *trace_id, ssize_t trace_id_len) { + return (ddtrace_trace_id){ + .high = trace_id_len > 16 ? ddtrace_parse_hex_span_id_str(trace_id, MIN(16, trace_id_len - 16)) : 0, + .low = ddtrace_parse_hex_span_id_str(trace_id + MAX(0, trace_id_len - 16), MIN(16, trace_id_len)), + }; +} + uint64_t ddtrace_parse_hex_span_id_str(const char *id, size_t len) { if (len == 0) { return 0U; diff --git a/ext/random.h b/ext/random.h index b8e9793bdd..3e924033e0 100644 --- a/ext/random.h +++ b/ext/random.h @@ -17,6 +17,7 @@ uint64_t ddtrace_peek_span_id(void); ddtrace_trace_id ddtrace_peek_trace_id(void); uint64_t ddtrace_parse_userland_span_id(zval *zid); ddtrace_trace_id ddtrace_parse_userland_trace_id(zend_string *tid); +ddtrace_trace_id ddtrace_parse_hex_trace_id(char *trace_id, ssize_t trace_id_len); uint64_t ddtrace_parse_hex_span_id_str(const char *id, size_t len); uint64_t ddtrace_parse_hex_span_id(zval *zid); int ddtrace_conv10_trace_id(ddtrace_trace_id id, uint8_t reverse[DD_TRACE_MAX_ID_LEN]); diff --git a/ext/serializer.c b/ext/serializer.c index 3363587cee..0f2077d8e8 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -654,6 +654,8 @@ void ddtrace_update_root_id_properties(ddtrace_root_span_data *span) { void ddtrace_set_root_span_properties(ddtrace_root_span_data *span) { ddtrace_update_root_id_properties(span); + span->sampling_rule.rule = INT32_MAX; + zend_array *meta = ddtrace_property_array(&span->property_meta); zend_hash_copy(meta, &DDTRACE_G(root_span_tags_preset), (copy_ctor_func_t)zval_add_ref); @@ -843,9 +845,7 @@ void ddtrace_set_root_span_properties(ddtrace_root_span_data *span) { if (DDTRACE_G(propagated_priority_sampling) != DDTRACE_PRIORITY_SAMPLING_UNSET) { ZVAL_LONG(&span->property_propagated_sampling_priority, DDTRACE_G(propagated_priority_sampling)); } - if (DDTRACE_G(default_priority_sampling) != DDTRACE_PRIORITY_SAMPLING_UNSET) { - ZVAL_LONG(&span->property_sampling_priority, DDTRACE_G(default_priority_sampling)); - } + ZVAL_LONG(&span->property_sampling_priority, DDTRACE_G(default_priority_sampling)); ddtrace_integration *web_integration = &ddtrace_integrations[DDTRACE_INTEGRATION_WEB]; if (get_DD_TRACE_ANALYTICS_ENABLED() || web_integration->is_analytics_enabled()) { @@ -1347,7 +1347,7 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) { zval_ptr_dtor(&prop_type_as_string); zval_ptr_dtor(&prop_resource_as_string); - if (ddtrace_fetch_priority_sampling_from_span(span->root) <= 0) { + if (zend_hash_num_elements(get_DD_SPAN_SAMPLING_RULES()) && ddtrace_fetch_priority_sampling_from_span(span->root) <= 0) { zval *rule; ZEND_HASH_FOREACH_VAL(get_DD_SPAN_SAMPLING_RULES(), rule) { if (Z_TYPE_P(rule) != IS_ARRAY) { diff --git a/ext/span.c b/ext/span.c index dc12e28d1b..cf85025863 100644 --- a/ext/span.c +++ b/ext/span.c @@ -576,10 +576,11 @@ void ddtrace_close_top_span_without_stack_swap(ddtrace_span_data *span) { stack->closed_ring = span; } + ddtrace_decide_on_closed_span_sampling(span); + if (!stack->active || SPANDATA(stack->active)->stack != stack) { dd_close_entry_span_of_stack(stack); } - } // i.e. what DDTrace\active_span() reports. DDTrace\active_stack()->active is the active span which will be used as parent for new spans on that stack diff --git a/ext/span.h b/ext/span.h index 28a10d7dc7..b31d32017c 100644 --- a/ext/span.h +++ b/ext/span.h @@ -22,6 +22,17 @@ enum ddtrace_span_dataype { DDTRACE_SPAN_CLOSED, }; +typedef struct { + double sampling_rate; + int rule; +} ddtrace_rule_result; + +enum ddtrace_trace_limited { + DD_TRACE_LIMIT_UNCHECKED, + DD_TRACE_LIMITED, + DD_TRACE_UNLIMITED, +}; + typedef union ddtrace_span_properties { zend_object std; struct { @@ -82,6 +93,9 @@ static inline ddtrace_span_data *SPANDATA(ddtrace_span_properties *obj) { struct ddtrace_root_span_data { ddtrace_trace_id trace_id; uint64_t parent_id; + ddtrace_rule_result sampling_rule; + bool explicit_sampling_priority; + enum ddtrace_trace_limited trace_is_limited; union { ddtrace_span_data; diff --git a/tests/Unit/Encoders/MessagePackTest.php b/tests/Unit/Encoders/MessagePackTest.php index 7d0e55de4d..a9b4fc76d0 100644 --- a/tests/Unit/Encoders/MessagePackTest.php +++ b/tests/Unit/Encoders/MessagePackTest.php @@ -42,14 +42,14 @@ protected function ddTearDown() dd_trace_internal_fn('ddtrace_reload_config'); } - public function testEncodeNoPrioritySampling() + public function testEncodeAlwaysHasPrioritySampling() { $span = $this->tracer->startRootSpan('test_name')->getSpan(); - $this->tracer->setPrioritySampling(\DD_TRACE_PRIORITY_SAMPLING_UNSET); + $this->tracer->setPrioritySampling(\DD_TRACE_PRIORITY_SAMPLING_UNKNOWN); $span->finish(); $encoder = new MessagePack(); - $this->assertStringNotContains('_sampling_priority_v1', $encoder->encodeTraces($this->tracer)); + $this->assertStringContains('_sampling_priority_v1', $encoder->encodeTraces($this->tracer)); } public function testEncodeMetricsWhenPresent() @@ -67,7 +67,6 @@ public function testEncodeMetricsWhenPresent() public function testAlwaysContainsDefaultMetrics() { $span = $this->tracer->startRootSpan('test_name')->getSpan(); - $this->tracer->setPrioritySampling(\DD_TRACE_PRIORITY_SAMPLING_UNSET); $span->finish(); $encoder = new MessagePack(); diff --git a/tests/Unit/TracerTest.php b/tests/Unit/TracerTest.php index 28d9f31cac..fb18059982 100644 --- a/tests/Unit/TracerTest.php +++ b/tests/Unit/TracerTest.php @@ -151,20 +151,11 @@ public function testPrioritySamplingIsEarlyAssignedAndRefreshedOnInject() { $tracer = new Tracer(new DebugTransport()); $span = $tracer->startRootSpan(self::OPERATION_NAME)->getSpan(); - $this->assertSame(PrioritySampling::USER_KEEP, $tracer->getPrioritySampling()); + $this->assertSame(PrioritySampling::AUTO_KEEP, $tracer->getPrioritySampling()); $span->metrics = []; $carrier = []; $tracer->inject($span->getContext(), Format::TEXT_MAP, $carrier); - $this->assertSame(PrioritySampling::USER_KEEP, $tracer->getPrioritySampling()); - } - - public function testPrioritySamplingIsLazilyAssignedAndRefreshedBeforeFlush() - { - $tracer = new Tracer(new DebugTransport()); - $span = $tracer->startRootSpan(self::OPERATION_NAME)->getSpan(); - $this->assertSame(PrioritySampling::USER_KEEP, $tracer->getPrioritySampling()); - $span->metrics = []; - $this->assertSame(PrioritySampling::USER_KEEP, $tracer->getPrioritySampling()); + $this->assertSame(PrioritySampling::AUTO_KEEP, $tracer->getPrioritySampling()); } public function testPrioritySamplingInheritedFromDistributedTracingContext() diff --git a/tests/ext/distributed_tracestate_consumption.phpt b/tests/ext/distributed_tracestate_consumption.phpt index 1bcf444f17..8a2edfe682 100644 --- a/tests/ext/distributed_tracestate_consumption.phpt +++ b/tests/ext/distributed_tracestate_consumption.phpt @@ -6,6 +6,7 @@ DD_TRACE_128_BIT_TRACEID_GENERATION_ENABLED=1 --FILE-- 0000000000000000000000000000002a [spanId] => %s - [traceState] => dd=o:datadog;t.custom_tag:inherited;t.second_tag:bar;t.dm:-0 + [traceState] => dd=o:datadog;t.custom_tag:inherited;t.second_tag:bar [attributes] => Array ( [_dd.p.custom_tag] => inherited [_dd.p.second_tag] => bar - [_dd.p.dm] => -0 ) ) diff --git a/tests/ext/integrations/curl/distributed_tracing_curl_propagate_tags.phpt b/tests/ext/integrations/curl/distributed_tracing_curl_propagate_tags.phpt index 1f2399d8da..498b4c8124 100644 --- a/tests/ext/integrations/curl/distributed_tracing_curl_propagate_tags.phpt +++ b/tests/ext/integrations/curl/distributed_tracing_curl_propagate_tags.phpt @@ -6,7 +6,7 @@ Distributed tracing header tags propagate with curl_exec() --ENV-- DD_TRACE_TRACED_INTERNAL_FUNCTIONS=curl_exec HTTP_X_DATADOG_TRACE_ID=42 -HTTP_X_DATADOG_TAGS=custom_tag=inherited,to_remove=,_dd.p.foo=bar,_dd.p.dm=abcdef-2 +HTTP_X_DATADOG_TAGS=custom_tag=inherited,to_remove=,_dd.p.foo=bar,_dd.p.dm=-2 --FILE-- --EXPECT-- -x-datadog-tags: _dd.p.foo=bar,_dd.p.dm=abcdef-2 +x-datadog-tags: _dd.p.foo=bar,_dd.p.dm=-4 diff --git a/tests/ext/pcntl/pcntl_fork_reseed_span_id.phpt b/tests/ext/pcntl/pcntl_fork_reseed_span_id.phpt index e0fad4e40d..5c536cc77d 100644 --- a/tests/ext/pcntl/pcntl_fork_reseed_span_id.phpt +++ b/tests/ext/pcntl/pcntl_fork_reseed_span_id.phpt @@ -49,8 +49,8 @@ call_httpbin(); echo 'Done.' . PHP_EOL; ?> --EXPECT-- +5969071622678286091 - parent 17952737041105130042 - parent -2376730765988821965 - parent 2469588189546311528 - child Done. 2516265689700432462 - parent diff --git a/tests/ext/priority_sampling/007-rule-service.phpt b/tests/ext/priority_sampling/007-rule-service.phpt index e657f5889f..2aced91cc2 100644 --- a/tests/ext/priority_sampling/007-rule-service.phpt +++ b/tests/ext/priority_sampling/007-rule-service.phpt @@ -5,7 +5,7 @@ DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.7, "service": "bar"},{"sample_rate": DD_TRACE_GENERATE_ROOT_SPAN=1 --SKIPIF-- diff --git a/tests/ext/priority_sampling/008-rule-service-reject.phpt b/tests/ext/priority_sampling/008-rule-service-reject.phpt index 8b50a0b7b5..a5913ae718 100644 --- a/tests/ext/priority_sampling/008-rule-service-reject.phpt +++ b/tests/ext/priority_sampling/008-rule-service-reject.phpt @@ -5,7 +5,7 @@ DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3, "service": "foo"}] DD_TRACE_GENERATE_ROOT_SPAN=1 --SKIPIF-- diff --git a/tests/ext/priority_sampling/010-rule-name-service-reject.phpt b/tests/ext/priority_sampling/010-rule-name-service-reject.phpt index ed36ac4c75..b8aba11e9a 100644 --- a/tests/ext/priority_sampling/010-rule-name-service-reject.phpt +++ b/tests/ext/priority_sampling/010-rule-name-service-reject.phpt @@ -5,7 +5,7 @@ DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3, "name": "no.*match", "service": "n DD_TRACE_GENERATE_ROOT_SPAN=1 --SKIPIF-- diff --git a/tests/ext/priority_sampling/012-default.phpt b/tests/ext/priority_sampling/012-default.phpt index d0939b5cdb..684792ff28 100644 --- a/tests/ext/priority_sampling/012-default.phpt +++ b/tests/ext/priority_sampling/012-default.phpt @@ -5,7 +5,7 @@ DD_TRACE_SAMPLING_RULES=[{"sample_rate": 1}] DD_TRACE_GENERATE_ROOT_SPAN=1 --FILE-- +--FILE-- +resource = "fooresource"; + +\DDTrace\get_priority_sampling(); + +if ($root->metrics["_dd.rule_psr"] == 0.3) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; +?> +--EXPECTREGEX-- +Rule OK +_dd.p.dm = (-3|-) diff --git a/tests/ext/priority_sampling/015-rule-resource-reject.phpt b/tests/ext/priority_sampling/015-rule-resource-reject.phpt new file mode 100644 index 0000000000..b6ae87d515 --- /dev/null +++ b/tests/ext/priority_sampling/015-rule-resource-reject.phpt @@ -0,0 +1,28 @@ +--TEST-- +priority_sampling rule with resource reject +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3, "resource": "foo"}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--SKIPIF-- + +--FILE-- +resource = "barresource"; + +\DDTrace\get_priority_sampling(); + +if (($root->metrics["_dd.rule_psr"] ?? 0) != 0.3 && $root->metrics["_dd.agent_psr"] == 1) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; +?> +--EXPECT-- +Rule OK +_dd.p.dm = -0 diff --git a/tests/ext/priority_sampling/016-rule-tag.phpt b/tests/ext/priority_sampling/016-rule-tag.phpt new file mode 100644 index 0000000000..f5dcb57561 --- /dev/null +++ b/tests/ext/priority_sampling/016-rule-tag.phpt @@ -0,0 +1,28 @@ +--TEST-- +priority_sampling rule with tag match +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.7, "tags": {"tag": "bar"}},{"sample_rate": 0.3, "tags": {"tag": "foo"}}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--SKIPIF-- + +--FILE-- +meta["tag"] = "foo"; + +\DDTrace\get_priority_sampling(); + +if ($root->metrics["_dd.rule_psr"] == 0.3) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; +?> +--EXPECTREGEX-- +Rule OK +_dd.p.dm = (-3|-) diff --git a/tests/ext/priority_sampling/017-rule-tag-reject.phpt b/tests/ext/priority_sampling/017-rule-tag-reject.phpt new file mode 100644 index 0000000000..c9adfd02dd --- /dev/null +++ b/tests/ext/priority_sampling/017-rule-tag-reject.phpt @@ -0,0 +1,28 @@ +--TEST-- +priority_sampling rule with tag reject +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3, "tags": {"missing": "bar"}}, {"sample_rate": 0.4, "tags": {"foo": "bar"}}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--SKIPIF-- + +--FILE-- +meta["foo"] = "different"; + +\DDTrace\get_priority_sampling(); + +if (($root->metrics["_dd.rule_psr"] ?? 0) != 0.3 && $root->metrics["_dd.agent_psr"] == 1) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; +?> +--EXPECT-- +Rule OK +_dd.p.dm = -0 diff --git a/tests/ext/priority_sampling/018-manual.drop.phpt b/tests/ext/priority_sampling/018-manual.drop.phpt new file mode 100644 index 0000000000..988f5135b2 --- /dev/null +++ b/tests/ext/priority_sampling/018-manual.drop.phpt @@ -0,0 +1,23 @@ +--TEST-- +priority_sampling with manual.drop +--ENV-- +DD_TRACE_SAMPLE_RATE=0 +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +meta["manual.drop"] = true; + +if (!isset($root->metrics["_dd.rule_psr"]) && \DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_REJECT) { + echo "OK\n"; +} else { + echo "metrics[_dd.rule_psr] = {$root->metrics["_dd.rule_psr"]}\n"; +} + +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; + +?> +--EXPECT-- +OK +_dd.p.dm = - diff --git a/tests/ext/priority_sampling/019-manual.keep.phpt b/tests/ext/priority_sampling/019-manual.keep.phpt new file mode 100644 index 0000000000..baed45183a --- /dev/null +++ b/tests/ext/priority_sampling/019-manual.keep.phpt @@ -0,0 +1,23 @@ +--TEST-- +priority_sampling with manual.keep +--ENV-- +DD_TRACE_SAMPLE_RATE=1 +DD_TRACE_GENERATE_ROOT_SPAN=1 +--FILE-- +meta["manual.keep"] = true; + +if (!isset($root->metrics["_dd.rule_psr"]) && \DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_KEEP) { + echo "OK\n"; +} else { + echo "metrics[_dd.rule_psr] = {$root->metrics["_dd.rule_psr"]}\n"; +} + +echo "_dd.p.dm = {$root->meta["_dd.p.dm"]}\n"; + +?> +--EXPECT-- +OK +_dd.p.dm = -4 diff --git a/tests/ext/priority_sampling/020-rule-nonroot.phpt b/tests/ext/priority_sampling/020-rule-nonroot.phpt new file mode 100644 index 0000000000..a56b2d006e --- /dev/null +++ b/tests/ext/priority_sampling/020-rule-nonroot.phpt @@ -0,0 +1,90 @@ +--TEST-- +priority_sampling rule with match on non-root spans +--ENV-- +DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.9, "tags": {"end": "true"}},{"sample_rate": 0.7, "service": "bar", "target_span": "any"},{"sample_rate": 0.3, "service": "foo"}] +DD_TRACE_GENERATE_ROOT_SPAN=1 +--SKIPIF-- + +--FILE-- +traceId = str_repeat("1", 32); + +var_dump(\DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_AUTO_KEEP); + +if ($root->metrics["_dd.agent_psr"] == 1) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; + +$root->service = "foo"; + +var_dump(\DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_KEEP); + +if (!isset($root->metrics["_dd.agent_psr"]) && $root->metrics["_dd.rule_psr"] == 0.3) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; + +$child = \DDTrace\start_span(); + +$child->service = "bar"; + +var_dump(\DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_KEEP); + +if (!isset($root->metrics["_dd.agent_psr"]) && $root->metrics["_dd.rule_psr"] == 0.7) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; + +\DDTrace\close_span(); + +var_dump(\DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_KEEP); + +if (!isset($root->metrics["_dd.agent_psr"]) && $root->metrics["_dd.rule_psr"] == 0.7) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +echo "_dd.p.dm = ", isset($root->meta["_dd.p.dm"]) ? $root->meta["_dd.p.dm"] : "-", "\n"; + +$root->meta["end"] = "true"; + +var_dump(\DDTrace\get_priority_sampling() == \DD_TRACE_PRIORITY_SAMPLING_USER_KEEP); + +if (!isset($root->metrics["_dd.agent_psr"]) && $root->metrics["_dd.rule_psr"] == 0.9) { + echo "Rule OK\n"; +} else { + var_dump($root->metrics); +} + +?> +--EXPECTF-- +bool(true) +Rule OK +_dd.p.dm = -0 +bool(true) +Rule OK +_dd.p.dm = -3 +bool(true) +Rule OK +_dd.p.dm = -3 +bool(true) +Rule OK +_dd.p.dm = -3 +bool(true) +Rule OK diff --git a/tests/ext/priority_sampling/invalid-regex-rule.phpt b/tests/ext/priority_sampling/invalid-regex-rule.phpt index 1abf25faea..2c63ae3c52 100644 --- a/tests/ext/priority_sampling/invalid-regex-rule.phpt +++ b/tests/ext/priority_sampling/invalid-regex-rule.phpt @@ -5,7 +5,7 @@ DD_TRACE_SAMPLING_RULES=[{"sample_rate": 0.3, "service": "*"}] DD_TRACE_GENERATE_ROOT_SPAN=1 --SKIPIF-- diff --git a/tests/ext/retrieve_128_bit_trace_id.phpt b/tests/ext/retrieve_128_bit_trace_id.phpt index 7dec326f46..c84af72be8 100644 --- a/tests/ext/retrieve_128_bit_trace_id.phpt +++ b/tests/ext/retrieve_128_bit_trace_id.phpt @@ -60,7 +60,7 @@ string(20) "13930160852258120406" string(32) "%sc151df7d6ee5e2d6" string(32) "%sa3978fb9b92502a8" string(32) "192f3581c8461c79abf2684ee31ce27d" -string(32) "%s22e2c43f8a1ad34e" +string(32) "%sc08c967f0e5e7b0a" string(20) "12390080212876714621" string(1) "1" string(32) "00000000000000010000000000000001" diff --git a/tests/ext/sandbox-regression/limiter_reset_flush_with_open_spans.phpt b/tests/ext/sandbox-regression/limiter_reset_flush_with_open_spans.phpt index 21c88967c2..5921cec36b 100644 --- a/tests/ext/sandbox-regression/limiter_reset_flush_with_open_spans.phpt +++ b/tests/ext/sandbox-regression/limiter_reset_flush_with_open_spans.phpt @@ -71,26 +71,26 @@ string(28) "current :2513787319205155662" string(28) "closing :2513787319205155662" Flushing trace of size 2 to send-queue for %s string(34) "newly active :13874630024467741450" -string(29) "initial :10598951352238613536" -string(28) "started :6878563960102566144" +string(28) "initial :1735254072534978428" +string(29) "started :10598951352238613536" bar baz baz() called bar() called -string(28) "current :6878563960102566144" -string(28) "closing :6878563960102566144" +string(29) "current :10598951352238613536" +string(29) "closing :10598951352238613536" Flushing trace of size 2 to send-queue for %s -string(34) "newly active :10598951352238613536" -string(27) "initial :228421809995595595" -string(28) "started :9660662969780974662" +string(33) "newly active :1735254072534978428" +string(28) "initial :5052085463162682550" +string(28) "started :7199227068870524257" bar baz baz() called bar() called -string(28) "current :9660662969780974662" -string(28) "closing :9660662969780974662" +string(28) "current :7199227068870524257" +string(28) "closing :7199227068870524257" Flushing trace of size 2 to send-queue for %s -string(32) "newly active :228421809995595595" +string(33) "newly active :5052085463162682550" foo() called Flushing trace of size 5 to send-queue for %s -No finished traces to be sent to the agent +No finished traces to be sent to the agent \ No newline at end of file diff --git a/tests/ext/single-span_sampling/check-sample-rate.phpt b/tests/ext/single-span_sampling/check-sample-rate.phpt index 59e385307c..1bc15df391 100644 --- a/tests/ext/single-span_sampling/check-sample-rate.phpt +++ b/tests/ext/single-span_sampling/check-sample-rate.phpt @@ -1,11 +1,11 @@ --TEST-- Check sample rate is in effect --SKIPIF-- - + --ENV-- DD_SAMPLING_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":0.5,"max_per_second":10}] -DD_TRACE_DEBUG_PRNG_SEED=420 +DD_TRACE_DEBUG_PRNG_SEED=30 DD_TRACE_GENERATE_ROOT_SPAN=0 --FILE-- + --ENV-- DD_SAMPLING_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":1,"max_per_second":2,"service":"a","name":"b"},{"sample_rate":1,"max_per_second":2,"service":"a"},{"sample_rate":1,"max_per_second":2,"name":"b"}] diff --git a/tests/ext/single-span_sampling/limited-single-span.phpt b/tests/ext/single-span_sampling/limited-single-span.phpt index c99a9085eb..1d4c6a4ddc 100644 --- a/tests/ext/single-span_sampling/limited-single-span.phpt +++ b/tests/ext/single-span_sampling/limited-single-span.phpt @@ -1,7 +1,7 @@ --TEST-- Test max_per_second single span limiting --SKIPIF-- - + --ENV-- DD_SAMPLING_RATE=0 DD_SPAN_SAMPLING_RULES=[{"sample_rate":1,"max_per_second":10}] From 8dc1a6fed83c2d5b8ccdeb95b4e3d2ce74720ac6 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Fri, 24 Nov 2023 11:45:25 +0100 Subject: [PATCH 2/5] Default sampling rate for any rule is 1 --- ext/priority_sampling/priority_sampling.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index 422194531b..0f831dfc06 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -144,9 +144,9 @@ static ddtrace_rule_result dd_match_rules(ddtrace_span_data *span, bool eval_roo } } - zval *sample_rate_zv; - if (dd_check_sampling_rule(Z_ARR_P(rule), span) && (sample_rate_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("sample_rate")))) { - return (ddtrace_rule_result){ .sampling_rate = zval_get_double(sample_rate_zv), .rule = index }; + if (dd_check_sampling_rule(Z_ARR_P(rule), span)) { + zval *sample_rate_zv = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("sample_rate")); + return (ddtrace_rule_result){ .sampling_rate = sample_rate_zv ? zval_get_double(sample_rate_zv) : 1, .rule = index }; } } ZEND_HASH_FOREACH_END(); From 5610535ed0e5e593d56f8c701645d103a050ab2c Mon Sep 17 00:00:00 2001 From: Pierre Bonet Date: Thu, 7 Dec 2023 15:55:16 +0100 Subject: [PATCH 3/5] Handle glob as input for rules Sampling rules now use glob pattern matching. We used to have regexes, so it's a breaking change. To work around this, we add a new DD_TRACE_SAMPLING_RULES_FORMAT parameter. This parameter allows `regex` and `glob`. `regex` is the default for retrocompatibility reasons. That said we will drop this parameter in 1.0. Glob matching was already implemented for single span samplling so I just factorized some code. --- ext/configuration.c | 13 ++++ ext/configuration.h | 7 ++ ext/ddshared.c | 74 ++++++++++++++++++- ext/ddshared.h | 2 + ext/priority_sampling/priority_sampling.c | 20 ++--- ext/serializer.c | 58 +-------------- ...-rule-name-as-glob-with-question-mark.phpt | 73 ++++++++++++++++++ 7 files changed, 176 insertions(+), 71 deletions(-) create mode 100644 tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt diff --git a/ext/configuration.c b/ext/configuration.c index 8866a8e37e..a333482d24 100644 --- a/ext/configuration.c +++ b/ext/configuration.c @@ -67,6 +67,19 @@ static bool dd_parse_dbm_mode(zai_str value, zval *decoded_value, bool persisten return true; } +static bool dd_parse_sampling_rules_format(zai_str value, zval *decoded_value, bool persistent) { + UNUSED(persistent); + if (zai_str_eq_ci_cstr(value, "regex")) { + ZVAL_LONG(decoded_value, DD_TRACE_SAMPLING_RULES_FORMAT_REGEX); + } else if (zai_str_eq_ci_cstr(value, "glob")) { + ZVAL_LONG(decoded_value, DD_TRACE_SAMPLING_RULES_FORMAT_GLOB); + } else { + return false; + } + + return true; +} + // Allow for partially defined struct initialization here #pragma GCC diagnostic ignored "-Wmissing-field-initializers" diff --git a/ext/configuration.h b/ext/configuration.h index cb289e11b5..aa1e4af662 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -23,6 +23,12 @@ enum ddtrace_dbm_propagation_mode { DD_TRACE_DBM_PROPAGATION_FULL, }; +// To remove in 1.0 +enum ddtrace_sampling_rules_format { + DD_TRACE_SAMPLING_RULES_FORMAT_REGEX, + DD_TRACE_SAMPLING_RULES_FORMAT_GLOB +}; + /* From the curl docs on CONNECT_TIMEOUT_MS: * If libcurl is built to use the standard system name resolver, that * portion of the transfer will still use full-second resolution for @@ -116,6 +122,7 @@ enum ddtrace_dbm_propagation_mode { CONFIG(INT, DD_TRACE_RATE_LIMIT, "0", .ini_change = zai_config_system_ini_change) \ CALIAS(DOUBLE, DD_TRACE_SAMPLE_RATE, "-1", CALIASES("DD_SAMPLING_RATE")) \ CONFIG(JSON, DD_TRACE_SAMPLING_RULES, "[]") \ + CONFIG(CUSTOM(INT), DD_TRACE_SAMPLING_RULES_FORMAT, "regex", .parser = dd_parse_sampling_rules_format) \ CONFIG(JSON, DD_SPAN_SAMPLING_RULES, "[]") \ CONFIG(STRING, DD_SPAN_SAMPLING_RULES_FILE, "", .ini_change = ddtrace_alter_sampling_rules_file_config) \ CONFIG(SET_LOWERCASE, DD_TRACE_HEADER_TAGS, "") \ diff --git a/ext/ddshared.c b/ext/ddshared.c index ed1dc95d37..ddb84bb6c7 100644 --- a/ext/ddshared.c +++ b/ext/ddshared.c @@ -1,9 +1,11 @@ #include #include +#include #include "ddshared.h" +#include "configuration.h" #include "ddtrace.h" -#include +#include "uri_normalization.h" ZEND_EXTERN_MODULE_GLOBALS(ddtrace); @@ -14,3 +16,73 @@ void ddshared_minit(void) { ddtrace_php_version = Z_STR_P(zend_get_constant_str(ZEND_STRL("PHP_VERSION"))); } + + bool dd_rule_matches(zval *pattern, zval *prop, int rulesFormat) { + if (Z_TYPE_P(pattern) != IS_STRING) { + return false; + } + if (Z_TYPE_P(prop) != IS_STRING) { + return true; // default case unset or null must be true, everything else is too then... + } + + if (rulesFormat == DD_TRACE_SAMPLING_RULES_FORMAT_GLOB) { + return dd_glob_rule_matches(pattern, Z_STR_P(prop)); + } + else { + return zai_match_regex(Z_STR_P(pattern), Z_STR_P(prop)); + } +} + +bool dd_glob_rule_matches(zval *pattern, zend_string* value) { + if (Z_TYPE_P(pattern) != IS_STRING) { + return false; + } + + char *p = Z_STRVAL_P(pattern); + char *s = ZSTR_VAL(value); + + int wildcards = 0; + while (*p) { + if (*(p++) == '*') { + ++wildcards; + } + } + + p = Z_STRVAL_P(pattern); + + ALLOCA_FLAG(use_heap) + char **backtrack_points = do_alloca(wildcards * 2 * sizeof(char *), use_heap); + int backtrack_idx = 0; + + while (*p) { + if (!*s) { + while (*p == '*') { + ++p; + } + free_alloca(backtrack_points, use_heap); + return !*p; + } + if (*s == *p || *p == '?') { + ++s, ++p; + } else if (*p == '*') { + backtrack_points[backtrack_idx++] = ++p; + backtrack_points[backtrack_idx++] = s; + } else { + do { + if (backtrack_idx > 0) { + backtrack_idx -= 2; + p = backtrack_points[backtrack_idx]; + s = ++backtrack_points[backtrack_idx + 1]; + } else { + free_alloca(backtrack_points, use_heap); + return false; + } + } while (!*s); + backtrack_idx += 2; + } + } + + free_alloca(backtrack_points, use_heap); + + return true; +} diff --git a/ext/ddshared.h b/ext/ddshared.h index 9373ffd4ef..1dc8217ea2 100644 --- a/ext/ddshared.h +++ b/ext/ddshared.h @@ -6,5 +6,7 @@ extern zend_string *ddtrace_php_version; void ddshared_minit(void); +bool dd_rule_matches(zval *pattern, zval *prop, int rulesFormat); +bool dd_glob_rule_matches(zval *pattern, zend_string* value); #endif // DD_TRACE_SHARED_H diff --git a/ext/priority_sampling/priority_sampling.c b/ext/priority_sampling/priority_sampling.c index 0f831dfc06..572b1b364d 100644 --- a/ext/priority_sampling/priority_sampling.c +++ b/ext/priority_sampling/priority_sampling.c @@ -9,6 +9,7 @@ #include "../configuration.h" #include "../limiter/limiter.h" +#include "ddshared.h" ZEND_EXTERN_MODULE_GLOBALS(ddtrace); @@ -50,17 +51,6 @@ static void dd_update_decision_maker_tag(ddtrace_root_span_data *root_span, enum } } -static bool dd_rule_matches(zval *pattern, zval *prop) { - if (Z_TYPE_P(pattern) != IS_STRING) { - return false; - } - if (Z_TYPE_P(prop) != IS_STRING) { - return true; // default case unset or null must be true, everything else is too then... - } - - return zai_match_regex(Z_STR_P(pattern), Z_STR_P(prop)); -} - static bool dd_check_sampling_rule(zend_array *rule, ddtrace_span_data *span) { zval *service = &span->property_service; @@ -71,18 +61,18 @@ static bool dd_check_sampling_rule(zend_array *rule, ddtrace_span_data *span) { if (!mapped_service) { mapped_service = service; } - if (!dd_rule_matches(rule_pattern, mapped_service)) { + if (!dd_rule_matches(rule_pattern, mapped_service, get_DD_TRACE_SAMPLING_RULES_FORMAT())) { return false; } } } if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("name")))) { - if (!dd_rule_matches(rule_pattern, &span->property_name)) { + if (!dd_rule_matches(rule_pattern, &span->property_name, get_DD_TRACE_SAMPLING_RULES_FORMAT())) { return false; } } if ((rule_pattern = zend_hash_str_find(rule, ZEND_STRL("resource")))) { - if (!dd_rule_matches(rule_pattern, &span->property_resource)) { + if (!dd_rule_matches(rule_pattern, &span->property_resource, get_DD_TRACE_SAMPLING_RULES_FORMAT())) { return false; } } @@ -96,7 +86,7 @@ static bool dd_check_sampling_rule(zend_array *rule, ddtrace_span_data *span) { if (!(value = zend_hash_find(meta, tag_name))) { return false; } - if (!dd_rule_matches(rule_pattern, value)) { + if (!dd_rule_matches(rule_pattern, value, get_DD_TRACE_SAMPLING_RULES_FORMAT())) { return false; } } diff --git a/ext/serializer.c b/ext/serializer.c index 0f2077d8e8..ad7527796d 100644 --- a/ext/serializer.c +++ b/ext/serializer.c @@ -31,6 +31,7 @@ #include "priority_sampling/priority_sampling.h" #include "span.h" #include "uri_normalization.h" +#include "ddshared.h" ZEND_EXTERN_MODULE_GLOBALS(ddtrace); @@ -1119,59 +1120,6 @@ static void _serialize_meta(zval *el, ddtrace_span_data *span) { } } -static bool dd_rule_matches(zval *pattern, zend_string* value) { - if (Z_TYPE_P(pattern) != IS_STRING) { - return false; - } - - char *p = Z_STRVAL_P(pattern); - char *s = ZSTR_VAL(value); - - int wildcards = 0; - while (*p) { - if (*(p++) == '*') { - ++wildcards; - } - } - p = Z_STRVAL_P(pattern); - - ALLOCA_FLAG(use_heap) - char **backtrack_points = do_alloca(wildcards * 2 * sizeof(char *), use_heap); - int backtrack_idx = 0; - - while (*p) { - if (!*s) { - while (*p == '*') { - ++p; - } - free_alloca(backtrack_points, use_heap); - return !*p; - } - if (*s == *p || *p == '?') { - ++s, ++p; - } else if (*p == '*') { - backtrack_points[backtrack_idx++] = ++p; - backtrack_points[backtrack_idx++] = s; - } else { - do { - if (backtrack_idx > 0) { - backtrack_idx -= 2; - p = backtrack_points[backtrack_idx]; - s = ++backtrack_points[backtrack_idx + 1]; - } else { - free_alloca(backtrack_points, use_heap); - return false; - } - } while (!*s); - backtrack_idx += 2; - } - } - - free_alloca(backtrack_points, use_heap); - - return true; -} - static HashTable dd_span_sampling_limiters; #if ZTS static pthread_rwlock_t dd_span_sampling_limiter_lock; @@ -1359,7 +1307,7 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) { zval *rule_service; if ((rule_service = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("service")))) { if (Z_TYPE_P(prop_service) > IS_NULL) { - rule_matches &= dd_rule_matches(rule_service, Z_STR(prop_service_as_string)); + rule_matches &= dd_glob_rule_matches(rule_service, Z_STR(prop_service_as_string)); } else { rule_matches &= false; } @@ -1367,7 +1315,7 @@ void ddtrace_serialize_span_to_array(ddtrace_span_data *span, zval *array) { zval *rule_name; if ((rule_name = zend_hash_str_find(Z_ARR_P(rule), ZEND_STRL("name")))) { if (Z_TYPE_P(prop_name) > IS_NULL) { - rule_matches &= dd_rule_matches(rule_name, Z_STR_P(prop_name)); + rule_matches &= dd_glob_rule_matches(rule_name, Z_STR_P(prop_name)); } else { rule_matches = false; } diff --git a/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt new file mode 100644 index 0000000000..691968e0d1 --- /dev/null +++ b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt @@ -0,0 +1,73 @@ +--TEST-- +priority_sampling rule with name match, using glob +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=1 +DD_TRACE_SAMPLING_RULES_FORMAT=glob +--FILE-- +name = $name; + + DDTrace\get_priority_sampling(); + + if ($root->metrics["_dd.rule_psr"] == ($matches ? 0.7 : 0.3)) { + echo "As expected, $pattern " . ($matches ? "matches" : "doesn't match") . " $name (name)\n"; + } else { + echo "$pattern " . ($matches ? "should have matched" : "shouldn't have matched") . " $name (service). Metrics found were: \n"; + var_dump($root->metrics); + } + + ini_set("datadog.trace_sampling_rules", '[{"service":"' . $pattern . '","sample_rate":0.7},{"sample_rate": 0.3]'); + + $root = \DDTrace\root_span(); + $root->service = $name; + + \DDTrace\get_priority_sampling(); + + if ($root->metrics["_dd.rule_psr"] == ($matches ? 0.7 : 0.3)) { + echo "As expected, $pattern " . ($matches ? "matches" : "doesn't match") . " $name (service)\n"; + } else { + echo "$pattern " . ($matches ? "should have matched" : "shouldn't have matched") . " $name (name). Metrics found were: \n"; + var_dump($root->metrics); + } +} +?> + +--EXPECT-- +As expected, fooname matches fooname (name) +As expected, fooname matches fooname (service) +As expected, fooname** matches fooname (name) +As expected, fooname** matches fooname (service) +As expected, **fooname matches fooname (name) +As expected, **fooname matches fooname (service) +As expected, * matches fooname (name) +As expected, * matches fooname (service) +As expected, ??????? matches fooname (name) +As expected, ??????? matches fooname (service) +As expected, *? matches fooname (name) +As expected, *? matches fooname (service) +As expected, ?* matches fooname (name) +As expected, ?* matches fooname (service) +As expected, f*o*e matches fooname (name) +As expected, f*o*e matches fooname (service) +As expected, f*o*m? matches fooname (name) +As expected, f*o*m? matches fooname (service) +As expected, f*x*m? doesn't match fooname (name) +As expected, f*x*m? doesn't match fooname (service) From 18864895d08d218aa17a60035d6926c1ccaad459 Mon Sep 17 00:00:00 2001 From: Pierre Bonet Date: Thu, 7 Dec 2023 17:24:33 +0100 Subject: [PATCH 4/5] Fix glob matching algo There was one issue when you were using only `?`. I've added a circuit break that fixes that issue and also improve the perf of the algo in the case of patterns with no `*` and shorter than the length of the input string. --- ext/ddshared.c | 9 +++++++++ .../021-rule-name-as-glob-with-question-mark.phpt | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/ext/ddshared.c b/ext/ddshared.c index ddb84bb6c7..0bb4c3a218 100644 --- a/ext/ddshared.c +++ b/ext/ddshared.c @@ -42,10 +42,19 @@ bool dd_glob_rule_matches(zval *pattern, zend_string* value) { char *s = ZSTR_VAL(value); int wildcards = 0; + int patternLength = 0; + int stringLength = ZSTR_LEN(value); while (*p) { if (*(p++) == '*') { ++wildcards; } + patternLength++; + } + + // If there are no wildcards, no need to go through the whole string if pattern is shorter than the input string + // Indeed wildcards (ie '*') can replace multiple characters while '?' canonly replace one + if (wildcards == 0 && patternLength < stringLength) { + return false; } p = Z_STRVAL_P(pattern); diff --git a/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt index 691968e0d1..5f05350d06 100644 --- a/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt +++ b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt @@ -12,6 +12,8 @@ $tests = [ ["**fooname", "fooname", true], ["*", "fooname", true], ["???????", "fooname", true], + ["??????", "fooname", false], + ["??", "fooname", false], ["*?", "fooname", true], ["?*", "fooname", true], ["f*o*e", "fooname", true], @@ -61,6 +63,10 @@ As expected, * matches fooname (name) As expected, * matches fooname (service) As expected, ??????? matches fooname (name) As expected, ??????? matches fooname (service) +As expected, ?????? doesn't match fooname (name) +As expected, ?????? doesn't match fooname (service) +As expected, ?? doesn't match fooname (name) +As expected, ?? doesn't match fooname (service) As expected, *? matches fooname (name) As expected, *? matches fooname (service) As expected, ?* matches fooname (name) From ef2686c44723db83084356ef1398259732dbe57d Mon Sep 17 00:00:00 2001 From: Pierre Bonet Date: Tue, 12 Dec 2023 10:10:47 +0100 Subject: [PATCH 5/5] More tests We test that sampling works on resources with the same glob patterns Also adds a test where we test all criterias in one sampling rule. --- ...-rule-name-as-glob-with-question-mark.phpt | 29 +++++++++++++- ...-name-as-glob-with-multiple-criterias.phpt | 40 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/ext/priority_sampling/022-rule-name-as-glob-with-multiple-criterias.phpt diff --git a/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt index 5f05350d06..63eb154bfa 100644 --- a/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt +++ b/tests/ext/priority_sampling/021-rule-name-as-glob-with-question-mark.phpt @@ -19,6 +19,7 @@ $tests = [ ["f*o*e", "fooname", true], ["f*o*m?", "fooname", true], ["f*x*m?", "fooname", false], + ]; foreach ($tests as list($pattern, $name, $matches)) { @@ -46,7 +47,21 @@ foreach ($tests as list($pattern, $name, $matches)) { if ($root->metrics["_dd.rule_psr"] == ($matches ? 0.7 : 0.3)) { echo "As expected, $pattern " . ($matches ? "matches" : "doesn't match") . " $name (service)\n"; } else { - echo "$pattern " . ($matches ? "should have matched" : "shouldn't have matched") . " $name (name). Metrics found were: \n"; + echo "$pattern " . ($matches ? "should have matched" : "shouldn't have matched") . " $name (service). Metrics found were: \n"; + var_dump($root->metrics); + } + + ini_set("datadog.trace_sampling_rules", '[{"resource":"' . $pattern . '","sample_rate":0.7},{"sample_rate": 0.3]'); + + $root = \DDTrace\root_span(); + $root->resource = $name; + + \DDTrace\get_priority_sampling(); + + if ($root->metrics["_dd.rule_psr"] == ($matches ? 0.7 : 0.3)) { + echo "As expected, $pattern " . ($matches ? "matches" : "doesn't match") . " $name (resource)\n"; + } else { + echo "$pattern " . ($matches ? "should have matched" : "shouldn't have matched") . " $name (resource). Metrics found were: \n"; var_dump($root->metrics); } } @@ -55,25 +70,37 @@ foreach ($tests as list($pattern, $name, $matches)) { --EXPECT-- As expected, fooname matches fooname (name) As expected, fooname matches fooname (service) +As expected, fooname matches fooname (resource) As expected, fooname** matches fooname (name) As expected, fooname** matches fooname (service) +As expected, fooname** matches fooname (resource) As expected, **fooname matches fooname (name) As expected, **fooname matches fooname (service) +As expected, **fooname matches fooname (resource) As expected, * matches fooname (name) As expected, * matches fooname (service) +As expected, * matches fooname (resource) As expected, ??????? matches fooname (name) As expected, ??????? matches fooname (service) +As expected, ??????? matches fooname (resource) As expected, ?????? doesn't match fooname (name) As expected, ?????? doesn't match fooname (service) +As expected, ?????? doesn't match fooname (resource) As expected, ?? doesn't match fooname (name) As expected, ?? doesn't match fooname (service) +As expected, ?? doesn't match fooname (resource) As expected, *? matches fooname (name) As expected, *? matches fooname (service) +As expected, *? matches fooname (resource) As expected, ?* matches fooname (name) As expected, ?* matches fooname (service) +As expected, ?* matches fooname (resource) As expected, f*o*e matches fooname (name) As expected, f*o*e matches fooname (service) +As expected, f*o*e matches fooname (resource) As expected, f*o*m? matches fooname (name) As expected, f*o*m? matches fooname (service) +As expected, f*o*m? matches fooname (resource) As expected, f*x*m? doesn't match fooname (name) As expected, f*x*m? doesn't match fooname (service) +As expected, f*x*m? doesn't match fooname (resource) diff --git a/tests/ext/priority_sampling/022-rule-name-as-glob-with-multiple-criterias.phpt b/tests/ext/priority_sampling/022-rule-name-as-glob-with-multiple-criterias.phpt new file mode 100644 index 0000000000..4c2589c93b --- /dev/null +++ b/tests/ext/priority_sampling/022-rule-name-as-glob-with-multiple-criterias.phpt @@ -0,0 +1,40 @@ +--TEST-- +priority_sampling rule with name match, using glob +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=1 +DD_TRACE_SAMPLING_RULES_FORMAT=glob +--FILE-- +service = "webserver"; + $root->name = "web.request"; + $root->resource = "/bar"; + + DDTrace\get_priority_sampling(); + + if ($root->metrics["_dd.rule_psr"] == ($matches ? 0.7 : 0.3)) { + echo "As expected, rule $servicePattern, $namePattern, $resourcePatthern " . ($matches ? "matches" : "doesn't match") . "\n"; + } else { + echo "Rule $servicePattern, $namePattern, $resourcePatthern " . ($matches ? "should have matched" : "shouldn't have matched") . "\n"; + var_dump($root->metrics); + } + +} +?> + +--EXPECT-- +As expected, rule webserver.non-matching, web.request, /bar doesn't match +As expected, rule webserver, web.request.non-matching, /bar doesn't match +As expected, rule webserver, web.request, /bar.non-matching doesn't match +As expected, rule webserver, web.request, /b?r matches