diff --git a/CHANGELOG.md b/CHANGELOG.md index 796c54fd4..b39aae7ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ **Fixes**: - Prevent backend state races when `sentry_reinstall_backend` runs concurrently with scope observer callbacks. ([#2041](https://github.com/getsentry/sentry-native/pull/2041)) +- `sentry_set_trace` omits `parent_span_id` when the caller does not provide one, instead of serializing it as `null`. ([#2047](https://github.com/getsentry/sentry-native/pull/2047)) ## 0.16.5 diff --git a/include/sentry.h b/include/sentry.h index f4e607566..b7626ddfc 100644 --- a/include/sentry.h +++ b/include/sentry.h @@ -2849,6 +2849,8 @@ SENTRY_API void sentry_scope_remove_fingerprint(sentry_scope_t *scope); * * Once a trace is managed by the downstream SDK using this function, * transactions no longer act as automatic trace boundaries. + * + * Pass a NULL or empty `parent_span_id` to start a trace without a parent. */ SENTRY_API void sentry_set_trace( const char *trace_id, const char *parent_span_id); diff --git a/src/sentry_core.c b/src/sentry_core.c index 31e453ea0..5b8f891d6 100644 --- a/src/sentry_core.c +++ b/src/sentry_core.c @@ -1367,8 +1367,10 @@ sentry_set_trace_n(const char *trace_id, size_t trace_id_len, sentry_value_set_by_key(context, "trace_id", sentry_value_new_string_n(trace_id, trace_id_len)); - sentry_value_set_by_key(context, "parent_span_id", - sentry_value_new_string_n(parent_span_id, parent_span_id_len)); + if (parent_span_id && parent_span_id_len) { + sentry_value_set_by_key(context, "parent_span_id", + sentry_value_new_string_n(parent_span_id, parent_span_id_len)); + } sentry_uuid_t span_id = sentry_uuid_new_v4(); sentry_value_set_by_key( diff --git a/tests/unit/test_tracing.c b/tests/unit/test_tracing.c index ae1cb9588..9ddb3672c 100644 --- a/tests/unit/test_tracing.c +++ b/tests/unit/test_tracing.c @@ -1584,6 +1584,39 @@ SENTRY_TEST(set_trace) sentry_close(); } +static void +check_trace_omits_parent(const char *parent_span_id, size_t parent_span_id_len) +{ + SENTRY_TEST_OPTIONS_NEW(options); + sentry_options_set_dsn(options, "https://foo@sentry.invalid/42"); + sentry_init(options); + + const char *trace_id = "2674eb52d5874b13b560236d6c79ce8a"; + sentry_set_trace_n( + trace_id, strlen(trace_id), parent_span_id, parent_span_id_len); + + SENTRY_WITH_SCOPE (scope) { + sentry_value_t propagation_trace_context + = sentry_value_get_by_key(scope->propagation_context, "trace"); + CHECK_STRING_PROPERTY(propagation_trace_context, "trace_id", trace_id); + + char *json = sentry_value_to_json(propagation_trace_context); + TEST_ASSERT(!!json); + TEST_CHECK(strstr(json, "parent_span_id") == NULL); + sentry_free(json); + } + + sentry_close(); +} + +SENTRY_TEST(set_trace_without_parent) +{ + check_trace_omits_parent(NULL, 0); + // A caller can pass a non-NULL parent span ID with length 0. + // Treat that the same as NULL. + check_trace_omits_parent("", 0); +} + sentry_value_t apply_scope_for_trace_context(sentry_options_t *options) { diff --git a/tests/unit/tests.inc b/tests/unit/tests.inc index bf5f7af10..5e5181488 100644 --- a/tests/unit/tests.inc +++ b/tests/unit/tests.inc @@ -418,6 +418,7 @@ XX(set_trace_id_twice) XX(set_trace_id_with_txn) XX(set_trace_rebuilds_dsc_sample_rand) XX(set_trace_update_from_header) +XX(set_trace_without_parent) XX(slice) XX(slice_consume_uint64) XX(span_data)