Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

**Features**:

- Add [strict trace continuation](https://develop.sentry.dev/sdk/foundations/trace-propagation/#strict-trace-continuation) via `sentry_options_set_strict_trace_continuation`. ([#1663](https://github.com/getsentry/sentry-native/pull/1663))
- Linux: support 32-bit ARM. ([#1659](https://github.com/getsentry/sentry-native/issues/1659))

**Fixes**:
Expand Down
46 changes: 46 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,48 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_propagate_traceparent(
SENTRY_EXPERIMENTAL_API int sentry_options_get_propagate_traceparent(
const sentry_options_t *opts);

/**
* Overrides the organization ID derived from the DSN host
* (e.g. `o123456.ingest.sentry.io` → `123456`). Typically only required for
* self-hosted setups where the DSN host does not encode the organization ID.
*
* The value is passed through as a string; no validation is performed.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_org_id(
sentry_options_t *opts, const char *org_id);
SENTRY_EXPERIMENTAL_API void sentry_options_set_org_id_n(
sentry_options_t *opts, const char *org_id, size_t org_id_len);

/**
* Returns the organization ID previously set via `sentry_options_set_org_id`,
* or NULL if none was set. Does not fall back to the DSN-derived value.
*/
SENTRY_EXPERIMENTAL_API const char *sentry_options_get_org_id(
const sentry_options_t *opts);

/**
* Enables or disables strict trace continuation.
*
* Controls whether to continue an incoming trace when either the trace or the
* SDK has an organization ID (derived from the DSN), but not both. When set
* to true, a new trace is started in that case; when false, the incoming
* trace is continued. If both organization IDs are present and differ, the
* trace is never continued regardless of this setting.
*
* See
* https://develop.sentry.dev/sdk/foundations/trace-propagation/#strict-trace-continuation
*
* This is disabled by default.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_strict_trace_continuation(
sentry_options_t *opts, int strict_trace_continuation);

/**
* Returns whether strict trace continuation is enabled.
*/
SENTRY_EXPERIMENTAL_API int sentry_options_get_strict_trace_continuation(
const sentry_options_t *opts);

/**
* Enables or disables the structured logging feature.
* When disabled, all calls to `sentry_log_X()` are no-ops.
Expand Down Expand Up @@ -2871,6 +2913,10 @@ SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled(
* services. Therefore, the headers of incoming requests should be fed into this
* function so that sentry is able to continue a trace that was started by an
* upstream service.
*
* Recognized header keys are `sentry-trace` and `baggage` (case-insensitive);
* other keys are ignored. Feed both when available so that strict trace
* continuation can consult the incoming `sentry-org_id`.
*/
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header(
sentry_transaction_context_t *tx_ctx, const char *key, const char *value);
Expand Down
96 changes: 58 additions & 38 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,39 +98,6 @@ generate_propagation_context(sentry_value_t propagation_context)
sentry_value_get_by_key(propagation_context, "trace"));
}

static void
set_dynamic_sampling_context(
const sentry_options_t *options, sentry_scope_t *scope)
{
sentry_value_decref(scope->dynamic_sampling_context);
// add the Dynamic Sampling Context to the `trace` header
sentry_value_t dsc = sentry_value_new_object();

if (options->dsn) {
sentry_value_set_by_key(dsc, "public_key",
sentry_value_new_string(options->dsn->public_key));
sentry_value_set_by_key(
dsc, "org_id", sentry_value_new_string(options->dsn->org_id));
}
sentry_value_set_by_key(dsc, "sample_rate",
sentry_value_new_double(options->traces_sample_rate));
if (options->traces_sampler) {
sentry_value_set_by_key(
dsc, "sample_rate", sentry_value_new_double(1.0));
}
sentry_value_t sample_rand = sentry_value_get_by_key(
sentry_value_get_by_key(scope->propagation_context, "trace"),
"sample_rand");
sentry_value_set_by_key(dsc, "sample_rand", sample_rand);
sentry_value_incref(sample_rand);
sentry_value_set_by_key(
dsc, "release", sentry_value_new_string(scope->release));
sentry_value_set_by_key(
dsc, "environment", sentry_value_new_string(scope->environment));

scope->dynamic_sampling_context = dsc;
}

#if defined(SENTRY_PLATFORM_NX) || defined(SENTRY_PLATFORM_PS)
int
sentry__native_init(sentry_options_t *options)
Expand Down Expand Up @@ -247,7 +214,7 @@ sentry_init(sentry_options_t *options)
sentry__ringbuffer_set_max_size(
scope->breadcrumbs, options->max_breadcrumbs);

set_dynamic_sampling_context(options, scope);
sentry__scope_rebuild_dsc_from_options(scope, options);
}
if (backend && backend->user_consent_changed_func) {
backend->user_consent_changed_func(backend);
Expand Down Expand Up @@ -1180,15 +1147,24 @@ sentry_set_trace_n(const char *trace_id, size_t trace_id_len,
sentry__generate_sample_rand(context);

sentry__set_propagation_context("trace", context);

SENTRY_WITH_OPTIONS (options) {
SENTRY_WITH_SCOPE_MUT (scope) {
sentry__scope_rebuild_dsc_from_options(scope, options);
}
}
}
}

void
sentry_regenerate_trace(void)
{
SENTRY_WITH_SCOPE_MUT (scope) {
generate_propagation_context(scope->propagation_context);
scope->trace_managed = false;
SENTRY_WITH_OPTIONS (options) {
SENTRY_WITH_SCOPE_MUT (scope) {
generate_propagation_context(scope->propagation_context);
scope->trace_managed = false;
sentry__scope_rebuild_dsc_from_options(scope, options);
}
Comment thread
jpnurmi marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -1260,6 +1236,50 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx,
sentry_value_remove_by_key(tx, "timestamp");

sentry__value_merge_objects(tx, tx_ctx);

sentry_value_t incoming = sentry_value_get_by_key(tx, "incoming_dsc");
if (!sentry_value_is_null(incoming)) {
SENTRY_WITH_OPTIONS (options) {
SENTRY_WITH_SCOPE_MUT (scope) {
const char *sdk_org
= sentry__options_get_effective_org_id(options);
const char *inc_org = sentry_value_as_string(
sentry_value_get_by_key(incoming, "org_id"));
if (!*inc_org) {
inc_org = NULL;
}

if (sentry__trace_continuation_allowed(
sdk_org, inc_org, options->strict_trace_continuation)) {
// Freeze only when the upstream actually sent DSC values;
// a sentry-trace-only signal leaves incoming empty, in
// which case the SDK builds its own DSC.
if (sentry_value_get_length(incoming) > 0) {
sentry__scope_freeze_dsc_from_incoming(scope, incoming);
} else {
sentry__scope_rebuild_dsc_from_options(scope, options);
}
} else {
// Fork: ignore upstream trace, become head of a new trace.
// Regenerate the scope's propagation context so events
// captured outside this transaction also carry the new
// trace_id, and align the tx's trace_id with it.
generate_propagation_context(scope->propagation_context);
sentry_value_t scope_trace_id = sentry_value_get_by_key(
sentry_value_get_by_key(
scope->propagation_context, "trace"),
"trace_id");
sentry_value_incref(scope_trace_id);
sentry_value_set_by_key(tx, "trace_id", scope_trace_id);
sentry_value_remove_by_key(tx, "parent_span_id");
sentry_value_remove_by_key(tx, "sampled");
sentry__scope_rebuild_dsc_from_options(scope, options);
Comment thread
cursor[bot] marked this conversation as resolved.
}
}
}
}
sentry_value_remove_by_key(tx, "incoming_dsc");

double sample_rand = 1.0;
SENTRY_WITH_SCOPE (scope) {
sample_rand = sentry_value_as_double(sentry_value_get_by_key(
Expand All @@ -1269,7 +1289,7 @@ sentry_transaction_start_ts(sentry_transaction_context_t *opaque_tx_ctx,
sentry_sampling_context_t sampling_ctx
= { opaque_tx_ctx, custom_sampling_ctx, NULL, sample_rand };

bool should_sample = sentry__should_send_transaction(tx_ctx, &sampling_ctx);
bool should_sample = sentry__should_send_transaction(tx, &sampling_ctx);
sentry_value_set_by_key(
tx, "sampled", sentry_value_new_bool(should_sample));
sentry_value_decref(custom_sampling_ctx);
Expand Down
48 changes: 48 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ sentry_options_new(void)
opts->enable_logging_when_crashed = true;
#endif
opts->propagate_traceparent = false;
opts->strict_trace_continuation = false;
Comment thread
jpnurmi marked this conversation as resolved.
opts->crashpad_limit_stack_capture_to_sp = false;
opts->enable_metrics = true;
opts->cache_keep = false;
Expand Down Expand Up @@ -122,6 +123,7 @@ sentry_options_free(sentry_options_t *opts)
sentry_free(opts->dist);
sentry_free(opts->proxy);
sentry_free(opts->ca_certs);
sentry_free(opts->org_id);
sentry_free(opts->transport_thread_name);
sentry__path_free(opts->database_path);
sentry__path_free(opts->handler_path);
Expand Down Expand Up @@ -218,6 +220,39 @@ sentry_options_get_dsn(const sentry_options_t *opts)
return opts->dsn ? opts->dsn->raw : NULL;
}

void
sentry_options_set_org_id_n(
sentry_options_t *opts, const char *org_id, size_t org_id_len)
{
sentry_free(opts->org_id);
opts->org_id = sentry__string_clone_n(org_id, org_id_len);
}

void
sentry_options_set_org_id(sentry_options_t *opts, const char *org_id)
{
sentry_free(opts->org_id);
opts->org_id = sentry__string_clone(org_id);
}

const char *
sentry_options_get_org_id(const sentry_options_t *opts)
{
return opts->org_id;
}

const char *
sentry__options_get_effective_org_id(const sentry_options_t *opts)
{
if (opts->org_id && *opts->org_id) {
return opts->org_id;
}
if (opts->dsn && opts->dsn->org_id && *opts->dsn->org_id) {
return opts->dsn->org_id;
}
return NULL;
}

void
sentry_options_set_sample_rate(sentry_options_t *opts, double sample_rate)
{
Expand Down Expand Up @@ -922,6 +957,19 @@ sentry_options_get_propagate_traceparent(const sentry_options_t *opts)
return opts->propagate_traceparent;
}

void
sentry_options_set_strict_trace_continuation(
sentry_options_t *opts, int strict_trace_continuation)
{
opts->strict_trace_continuation = !!strict_trace_continuation;
}

int
sentry_options_get_strict_trace_continuation(const sentry_options_t *opts)
{
return opts->strict_trace_continuation;
}

void
sentry_options_set_send_client_reports(sentry_options_t *opts, int val)
{
Expand Down
10 changes: 10 additions & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct sentry_options_s {
bool crashpad_wait_for_upload;
bool enable_logging_when_crashed;
bool propagate_traceparent;
bool strict_trace_continuation;
bool crashpad_limit_stack_capture_to_sp;
bool cache_keep;

Expand All @@ -72,6 +73,7 @@ struct sentry_options_s {
double traces_sample_rate;
sentry_traces_sampler_function traces_sampler;
void *traces_sampler_data;
char *org_id;
size_t max_spans;
bool enable_logs;
// takes the first varg as a `sentry_value_t` object containing attributes
Expand Down Expand Up @@ -107,4 +109,12 @@ struct sentry_options_s {
*/
sentry_options_t *sentry__options_incref(sentry_options_t *options);

/**
* Returns the effective organization ID used for trace propagation:
* the `org_id` option if set and non-empty, otherwise the DSN-derived value
* if non-empty, otherwise NULL.
*/
const char *sentry__options_get_effective_org_id(
const sentry_options_t *options);

#endif
45 changes: 45 additions & 0 deletions src/sentry_scope.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,51 @@ sentry__scope_free(sentry_scope_t *scope)
sentry_free(scope);
}

void
sentry__scope_freeze_dsc_from_incoming(
sentry_scope_t *scope, sentry_value_t incoming)
{
sentry_value_decref(scope->dynamic_sampling_context);
sentry_value_t dsc = sentry_value_new_object();
sentry__value_merge_objects(dsc, incoming);
Comment thread
sentry[bot] marked this conversation as resolved.
sentry_value_freeze(dsc);
scope->dynamic_sampling_context = dsc;
}

void
sentry__scope_rebuild_dsc_from_options(
sentry_scope_t *scope, const sentry_options_t *options)
{
sentry_value_decref(scope->dynamic_sampling_context);
sentry_value_t dsc = sentry_value_new_object();

if (options->dsn) {
sentry_value_set_by_key(dsc, "public_key",
sentry_value_new_string(options->dsn->public_key));
}
const char *org_id = sentry__options_get_effective_org_id(options);
if (org_id) {
sentry_value_set_by_key(dsc, "org_id", sentry_value_new_string(org_id));
}
sentry_value_set_by_key(dsc, "sample_rate",
sentry_value_new_double(options->traces_sample_rate));
if (options->traces_sampler) {
sentry_value_set_by_key(
dsc, "sample_rate", sentry_value_new_double(1.0));
}
sentry_value_t sample_rand = sentry_value_get_by_key(
sentry_value_get_by_key(scope->propagation_context, "trace"),
"sample_rand");
sentry_value_set_by_key(dsc, "sample_rand", sample_rand);
sentry_value_incref(sample_rand);
sentry_value_set_by_key(
dsc, "release", sentry_value_new_string(scope->release));
sentry_value_set_by_key(
dsc, "environment", sentry_value_new_string(scope->environment));

scope->dynamic_sampling_context = dsc;
}

#if !defined(SENTRY_PLATFORM_NX)
static void
sentry__foreach_stacktrace(
Expand Down
16 changes: 16 additions & 0 deletions src/sentry_scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,22 @@ void sentry__scope_remove_attribute_n(
for (sentry_scope_t *Scope = sentry__scope_lock(); Scope; \
sentry__scope_unlock(), Scope = NULL)

/**
* Rebuilds the scope's dynamic sampling context (DSC) from the SDK options
* and the current propagation context. The previous DSC is discarded.
*/
void sentry__scope_rebuild_dsc_from_options(
sentry_scope_t *scope, const sentry_options_t *options);

/**
* Replaces the scope's dynamic sampling context (DSC) with a verbatim copy
* of the incoming object. Used when continuing an upstream trace: per the
* trace-propagation spec, the receiving SDK MUST treat the incoming DSC as
* frozen and propagate its values "as is".
*/
void sentry__scope_freeze_dsc_from_incoming(
sentry_scope_t *scope, sentry_value_t incoming);

/**
* Adds scoped attributes to the telemetry attributes object.
*/
Expand Down
Loading
Loading