From e79e54a5400fd5035b03fa7132dc3e7baf2fd6de Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 22 Nov 2024 08:29:11 +0100 Subject: [PATCH 01/12] Propagated Sampling Rates --- .../traces/dynamic-sampling-context.mdx | 5 +- develop-docs/sdk/telemetry/traces/index.mdx | 56 +++++++++++++++++-- 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx index 50be047547c85..0d996680fc09a 100644 --- a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx +++ b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx @@ -49,7 +49,8 @@ To align DSC propagation over all our SDKs, we defined a [unified propagation me All of the attributes in the table below are required (non-optional) in a sense, that when they are known to an SDK at the time an envelope with an event (transaction or error) is sent to Sentry, or at the time a baggage header is propagated, they must also be included in said envelope or baggage. At the moment, only `release`, `environment` and `transaction` are used by the product for dynamic sampling functionality. -The rest of the context attributes, `trace_id`, `public_key`, and `sample_rate`, are used by Relay for internal decisions (like transaction sample rate smoothing). +The rest of the context attributes, `trace_id`, `public_key`, `sampled` and `sample_rate`, are used by Relay for internal decisions (like transaction sample rate smoothing). +Additional entries such as `replay_id`, `org` and `sample_rand` are only using the DSC as a mean of transport. | Attribute | Type | Description | Example | Required Level | | --------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------ | @@ -60,6 +61,8 @@ The rest of the context attributes, `trace_id`, `public_key`, and `sample_rate`, | `release` | string | The release name as specified in client options. | `myapp@1.2.3`, `1.2.3`, `2025.4.107` | required | | `environment` | string | The environment name as specified in client options. | `production`, `staging` | required | | `transaction` | string | The transaction name set on the scope. **Only include** if name has [good quality](#note-on-good-quality-transaction-names). | `/login`, `myApp.myController.login` | required (if known and good quality) | +| `org` | string | The org ID parsed from the DSN or received by a downstream SDK. | `1` | required | +| `sample_rand` | string | A trully random number originating from the head of trace SDK. | `0.5` | required | | `user_segment` [DEPRECATED] | string | User segment as set by the user with `scope.set_user()`. | | deprecated | 0: In any case, `trace_id`, `public_key`, and `sample_rate` should always be known to an SDK, so these values are strictly required. diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index a9fc9d314d31a..04f5b96eb6aeb 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -17,25 +17,25 @@ Reference implementations: This section describes the options SDKs should expose to configure tracing and performance monitoring. -Tracing is enabled by setting any of three SDK config options, `enableTracing`, `tracesSampleRate` and `tracesSampler`. If not set, these options default to `undefined`, making tracing opt-in. +Tracing is enabled by setting either a `tracesSampleRate` or `tracesSampler`. If not set, these options default to `undefined` or `null`, making tracing opt-in. ### `enableTracing` -This option shall enable the generation of transactions and propagation of trace data. Sample rates shall be set at a default which is practical to the specific platform. Users may use the other options, listed below, should their use case require it. The standard should be to set the default sample rate at 100%, and only working back if there are inherent concerns for that platform. Users should be able to send most if not all of their data and rely on Sentry server side processing of their data. +This option is **deprecated** and should be removed from all SDKs. ### `tracesSampleRate` -This should be a float/double between `0.0` and `1.0` (inclusive) and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](https://develop.sentry.dev/sdk/performance/#sampling), `0.0` is a 0% chance (none will be sent) and `1.0` is a 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction should have the same random chance of ending up with `sampled = true`, equal to the `tracesSampleRate`. +This should be a float/double between `0.0` and `1.0` (inclusive) and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](#sampling), `0.0` is a 0% chance (none will be sent) and `1.0` is a 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction should have the same random chance of ending up with `sampled = true`, equal to the `tracesSampleRate`. -See more about how sampling should be performed below. +See more about how sampling should be performed [below](#sampling). ### `tracesSampler` This should be a callback, called when a transaction is started, which will be given a `samplingContext` object and which should return a sample rate between `0.0` and `1.0` _for the transaction in question_. This sample rate should behave the same way as the `tracesSampleRate` above, with the difference that it only applies to the newly-created transaction, such that different transactions can be sampled at different rates. Returning `0.0` should force the transaction to be dropped (set to `sampled = false`) and returning `1.0` should force the transaction to be sent (set `sampled = true`). -Optionally, the `tracesSampler` callback can also return a boolean to force a sampling decision (with `false` equivalent to `0.0` and `true` equivalent to `1.0`). If returning two different datatypes isn't an option in the implementing language, this possibility can safely be omitted. +Historically, the `tracesSampler` callback could have also returned a boolean to force a sampling decision (with `false` equivalent to `0.0` and `true` equivalent to `1.0`). This behavior is now **deprecated** and should be removed from all SDKs. -See more about how sampling should be performed below. +See more about how sampling should be performed [below](#sampling). ### `tracePropagationTargets` @@ -65,6 +65,20 @@ This Option replaces the non-standardized `tracingOrigins` option which was prev +### `strictTracePropagation` + +This option disables trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. + +If the SDK can parse an org ID from the configured DSN, this value must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. + +On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN. Only if both match, the parent sampling decisions applies. + +This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. +Initially, SDKs should introduce the this option with a default values of `false`. +Once the majority of SDKs introduced this option, we will make it opt-out in a major version. + +The SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. This option should be set when running a self-hosted version of Sentry or if a none standard Sentry DSN is used, such as when using a local Relay. + ### `traceOptionsRequests` This should be a boolean value. Default is `false`. When set to `true` transactions should be created for HTTP `OPTIONS` requests. When set to `false` NO transactions should be created for HTTP `OPTIONS` requests. This configuration is most valuable on backend server SDKs. If this configuration does not make sense for an SDK it can be omitted. @@ -176,6 +190,7 @@ Transactions should be sampled only by `tracesSampleRate` or `tracesSampler`. Th If defined, the `tracesSampler` callback should be passed a `samplingContext` object, which should include, at minimum: - The `transactionContext` with which the transaction was created +- A float/double `parentSampleRate` which contains the sampling rate passed down from the parent - A boolean `parentSampled` which contains the sampling decision passed down from the parent, if any - Data from an optional `customSamplingContext` object passed to `startTransaction` when it is called manually @@ -185,6 +200,35 @@ Depending on the platform, other default data may be included. (For example, for A transaction's sampling decision should be passed to all of its children, including across service boundaries. This can be accomplished in the `startChild` method for same-service children and using the `senry-trace` header for children in a different service. +### Propagated Sampling Rates + +To increase the chance of capturing complete traces when users return a new sample rate from the `tracesSampler` in backend services, we propagate the generated random value used by the SDK for computing the sampling decision instead of creating a new random value in every service. Therefore, across a trace every SDK uses the same random value. + +The behavior of the static `tracesSampleRate` without the use of `tracesSampler` does not change. We continue to fully inherit sampling decisions for propagated traces and create a new one for started traces. In the future, we might change the default behavior of `tracesSampleRate`, too. + +Once the SDK starts a new trace, `sentry-sample_rand` is filled with a truly random number. This also applies when the `tracesSampleRate` is set to `1.0`. If the SDK receives an incoming trace, containing a `sentry-sample_rand` entry in the baggage, this value should overwrite the currently stored number. For incoming traces without a `sentry-sample_rand` baggage entry (from old SDKs), the SDK inserts a new truly random number on-the-fly. + +The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base. + +- When the `tracesSampler` is invoked, this also applies to the return value of traces sampler. i.e. `trace["sentry-sample_rand"] < tracesSampler(context)` +- Otherwise, when the SDK is the head of a trace, this applies to sample decisions based on `tracesSampleRate` , i.e. `trace["sentry-sample_rand"] < config.tracesSampleRate` + +When using a `tracesSampler`, the most correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, we can still extrapolate counts correctly. + +```js +tracesSampler: ({ name, parentSampleRate }) => { + // Inherit the trace parent's sample rate if there is one. Sampling is deterministic + // for one trace, i.e. if the parent was sampled, we will be sampled too at the same + // rate. + if (typeof parentSampleRate === "number") { + return parentSampleRate; + } + + // Else, use default sample rate (replacing tracesSampleRate). + return 0.5; +}, +``` + ### Backpressure If the SDK supports backpressure handling, the overall sampling rate needs to be divided by the `downsamplingFactor` from the backpressure monitor. See [the backpressure spec](/sdk/performance/backpressure/#downsampling) for more details. From c93add79c8da27cec76ea7be298b581bd8316424 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 22 Nov 2024 09:13:06 +0100 Subject: [PATCH 02/12] Clarify org setting --- develop-docs/sdk/telemetry/traces/index.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index 04f5b96eb6aeb..010ac73ba50a5 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -71,7 +71,8 @@ This option disables trace continuation from unknown 3rd party services that hap If the SDK can parse an org ID from the configured DSN, this value must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. -On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN. Only if both match, the parent sampling decisions applies. +On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN. Only if both match, the trace is continued. If there is no match, neither the trace ID, the parent sampling decision nor the baggage should be taken into account. +The SDK should behave like it is the head of trace in this case. This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. Initially, SDKs should introduce the this option with a default values of `false`. From 2e30a1cfd533e397e46346dd28e7a899f7a9a7a5 Mon Sep 17 00:00:00 2001 From: Stephanie Anderson Date: Tue, 26 Nov 2024 10:05:13 +0100 Subject: [PATCH 03/12] Apply wording suggestions from code review Co-authored-by: Ivana Kellyer Co-authored-by: Anton Pirker --- .../telemetry/traces/dynamic-sampling-context.mdx | 4 ++-- develop-docs/sdk/telemetry/traces/index.mdx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx index 0d996680fc09a..097cb24a3b66e 100644 --- a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx +++ b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx @@ -50,7 +50,7 @@ All of the attributes in the table below are required (non-optional) in a sense, At the moment, only `release`, `environment` and `transaction` are used by the product for dynamic sampling functionality. The rest of the context attributes, `trace_id`, `public_key`, `sampled` and `sample_rate`, are used by Relay for internal decisions (like transaction sample rate smoothing). -Additional entries such as `replay_id`, `org` and `sample_rand` are only using the DSC as a mean of transport. +Additional entries such as `replay_id`, `org` and `sample_rand` are only using the DSC as a means of transport. | Attribute | Type | Description | Example | Required Level | | --------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------ | @@ -62,7 +62,7 @@ Additional entries such as `replay_id`, `org` and `sample_rand` are only using t | `environment` | string | The environment name as specified in client options. | `production`, `staging` | required | | `transaction` | string | The transaction name set on the scope. **Only include** if name has [good quality](#note-on-good-quality-transaction-names). | `/login`, `myApp.myController.login` | required (if known and good quality) | | `org` | string | The org ID parsed from the DSN or received by a downstream SDK. | `1` | required | -| `sample_rand` | string | A trully random number originating from the head of trace SDK. | `0.5` | required | +| `sample_rand` | string | A truly random number originating from the head of trace SDK. | `0.5` | required | | `user_segment` [DEPRECATED] | string | User segment as set by the user with `scope.set_user()`. | | deprecated | 0: In any case, `trace_id`, `public_key`, and `sample_rate` should always be known to an SDK, so these values are strictly required. diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index 010ac73ba50a5..84d20dfad2f0d 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -69,16 +69,16 @@ This Option replaces the non-standardized `tracingOrigins` option which was prev This option disables trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. -If the SDK can parse an org ID from the configured DSN, this value must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. +If the SDK can parse an org ID from the configured DSN, it must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN. Only if both match, the trace is continued. If there is no match, neither the trace ID, the parent sampling decision nor the baggage should be taken into account. The SDK should behave like it is the head of trace in this case. This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. -Initially, SDKs should introduce the this option with a default values of `false`. -Once the majority of SDKs introduced this option, we will make it opt-out in a major version. +Initially, SDKs should introduce the this option with a default value of `false`. +Once the majority of SDKs introduced this option, we will change the default value to `true` (in a major version bump) making it opt-out. -The SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. This option should be set when running a self-hosted version of Sentry or if a none standard Sentry DSN is used, such as when using a local Relay. +The SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. This option should be set when running a self-hosted version of Sentry or if a non-standard Sentry DSN is used, such as when using a local Relay. ### `traceOptionsRequests` @@ -207,14 +207,14 @@ To increase the chance of capturing complete traces when users return a new samp The behavior of the static `tracesSampleRate` without the use of `tracesSampler` does not change. We continue to fully inherit sampling decisions for propagated traces and create a new one for started traces. In the future, we might change the default behavior of `tracesSampleRate`, too. -Once the SDK starts a new trace, `sentry-sample_rand` is filled with a truly random number. This also applies when the `tracesSampleRate` is set to `1.0`. If the SDK receives an incoming trace, containing a `sentry-sample_rand` entry in the baggage, this value should overwrite the currently stored number. For incoming traces without a `sentry-sample_rand` baggage entry (from old SDKs), the SDK inserts a new truly random number on-the-fly. +Once the SDK starts a new trace, `sentry-sample_rand` is filled with a truly random number. This also applies when the `tracesSampleRate` is set to `1.0`. If the SDK receives an incoming trace, containing a `sentry-sample_rand` entry in the baggage, this value should overwrite the currently stored number. For incoming traces without a `sentry-sample_rand` baggage entry (from old SDKs), the SDK inserts a new truly random number on the fly. The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base. - When the `tracesSampler` is invoked, this also applies to the return value of traces sampler. i.e. `trace["sentry-sample_rand"] < tracesSampler(context)` - Otherwise, when the SDK is the head of a trace, this applies to sample decisions based on `tracesSampleRate` , i.e. `trace["sentry-sample_rand"] < config.tracesSampleRate` -When using a `tracesSampler`, the most correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, we can still extrapolate counts correctly. +When using a `tracesSampler`, the correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, we can still extrapolate counts correctly. ```js tracesSampler: ({ name, parentSampleRate }) => { From 36711c7ca63113b153e87c3ca3a89fe7f88619ac Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Tue, 26 Nov 2024 13:34:21 +0100 Subject: [PATCH 04/12] fix: DSC invariant --- .../sdk/telemetry/traces/dynamic-sampling-context.mdx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx index 097cb24a3b66e..2c65b7a7f494d 100644 --- a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx +++ b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx @@ -49,20 +49,20 @@ To align DSC propagation over all our SDKs, we defined a [unified propagation me All of the attributes in the table below are required (non-optional) in a sense, that when they are known to an SDK at the time an envelope with an event (transaction or error) is sent to Sentry, or at the time a baggage header is propagated, they must also be included in said envelope or baggage. At the moment, only `release`, `environment` and `transaction` are used by the product for dynamic sampling functionality. -The rest of the context attributes, `trace_id`, `public_key`, `sampled` and `sample_rate`, are used by Relay for internal decisions (like transaction sample rate smoothing). +The rest of the context attributes, `trace_id`, `public_key`, `sampled` and `sample_rate`, are used by Relay for internal decisions and for extrapolation in the product. Additional entries such as `replay_id`, `org` and `sample_rand` are only using the DSC as a means of transport. | Attribute | Type | Description | Example | Required Level | | --------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ------------------------------------ | | `trace_id` | string | The original trace ID as generated by the SDK. This must match the trace id of the submitted transaction item. [1] | `771a43a4192642f0b136d5159a501700` | strictly required [0] | | `public_key` | string | Public key from the DSN used by the SDK. [2] | `49d0f7386ad645858ae85020e393bef3` | strictly required [0] | -| `sample_rate` | string | The sample rate as defined by the user on the SDK. [3] | `0.7` | strictly required [0] | -| `sampled` | string | `"true"` if the trace is sampled, `"false"` otherwise. This is set by the head of the trace. | `true` | required | +| `sample_rate` | string | The sample rate as defined by the user on the SDK. [3] [4] | `0.7` | strictly required [0] | +| `sample_rand` | string | A truly random number originating from the head of trace SDK. [4] | `0.5` | required | +| `sampled` | string | `"true"` if the trace is sampled, `"false"` otherwise. This is set by the head of the trace. [4] | `true` | required | | `release` | string | The release name as specified in client options. | `myapp@1.2.3`, `1.2.3`, `2025.4.107` | required | | `environment` | string | The environment name as specified in client options. | `production`, `staging` | required | | `transaction` | string | The transaction name set on the scope. **Only include** if name has [good quality](#note-on-good-quality-transaction-names). | `/login`, `myApp.myController.login` | required (if known and good quality) | | `org` | string | The org ID parsed from the DSN or received by a downstream SDK. | `1` | required | -| `sample_rand` | string | A truly random number originating from the head of trace SDK. | `0.5` | required | | `user_segment` [DEPRECATED] | string | User segment as set by the user with `scope.set_user()`. | | deprecated | 0: In any case, `trace_id`, `public_key`, and `sample_rate` should always be known to an SDK, so these values are strictly required. @@ -73,6 +73,8 @@ Additional entries such as `replay_id`, `org` and `sample_rand` are only using t 3: This string should always be a number between (and including) 0 and 1 in a notation that is supported by the [JSON specification](https://www.json.org/json-en.html). If a `tracesSampler` callback was used for the sampling decision, its result should be used for `sample_rate` instead of the `tracesSampleRate` from `SentryOptions`. In case `tracesSampler` returns `True` it should be sent as `1.0`, `False` should be sent as `0.0`. +4: These attributes must conform to the invariant `sample_rand < sample_rate <=> sampled`. + ### Note on good-quality transaction names @@ -279,5 +281,4 @@ TODO - Add some sort of Q&A section on the following questions, after evaluating - Why must baggage be immutable before the second transaction has been started? - What are the consequences and impacts of the immutability of baggage on Dynamic Sampling UX? - Why can't we just make the decision for the whole trace in Relay after the trace is complete? -- What is sample rate smoothing and how does it use `sample_rate` from the Dynamic Sampling Context? - What are the differences between Dynamic Sampling on traces vs. transactions? From ad4563650339700bf1955a5c6f3e797d8a9cd116 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Tue, 26 Nov 2024 14:10:10 +0100 Subject: [PATCH 05/12] More rules --- develop-docs/sdk/telemetry/traces/index.mdx | 44 ++++++++++++++------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index 84d20dfad2f0d..b3d3dca4ae10a 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -10,9 +10,16 @@ This should give an overview of the APIs that SDKs need to implement, without mandating internal implementation details. Reference implementations: + - [JavaScript SDK](https://github.com/getsentry/sentry-javascript/tree/master/packages/core/src/tracing) - [Python SDK](https://github.com/getsentry/sentry-python/blob/master/sentry_sdk/tracing.py) + + +This document uses standard interval notation, where `[` and `]` denote closed intervals which include the endpoints of the interval, while `(` and `)` denote open intervals which exclude the endpoints of the interval. An interval `[x, y)` covers all values starting from `x` up to but excluding `y`. + + + ## SDK Configuration This section describes the options SDKs should expose to configure tracing and performance monitoring. @@ -25,13 +32,13 @@ This option is **deprecated** and should be removed from all SDKs. ### `tracesSampleRate` -This should be a float/double between `0.0` and `1.0` (inclusive) and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](#sampling), `0.0` is a 0% chance (none will be sent) and `1.0` is a 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction should have the same random chance of ending up with `sampled = true`, equal to the `tracesSampleRate`. +This should be a float point number in the range `[0, 1]` and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](#sampling), `0.0` is a guaranteed 0% chance (none will be sent) and `1.0` is a guaranteed 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction should have the same random chance of ending up with `sampled = true`, equal to the `tracesSampleRate`. See more about how sampling should be performed [below](#sampling). ### `tracesSampler` -This should be a callback, called when a transaction is started, which will be given a `samplingContext` object and which should return a sample rate between `0.0` and `1.0` _for the transaction in question_. This sample rate should behave the same way as the `tracesSampleRate` above, with the difference that it only applies to the newly-created transaction, such that different transactions can be sampled at different rates. Returning `0.0` should force the transaction to be dropped (set to `sampled = false`) and returning `1.0` should force the transaction to be sent (set `sampled = true`). +This should be a callback, called when a transaction is started, which will be given a `samplingContext` object and which should return a sample rate in range `[0, 1]` _for the transaction in question_. This sample rate should behave the same way as the `tracesSampleRate` above, with the difference that it only applies to the newly-created transaction, such that different transactions can be sampled at different rates. Returning `0.0` should force the transaction to be dropped (set to `sampled = false`) and returning `1.0` should force the transaction to be sent (set `sampled = true`). Historically, the `tracesSampler` callback could have also returned a boolean to force a sampling decision (with `false` equivalent to `0.0` and `true` equivalent to `1.0`). This behavior is now **deprecated** and should be removed from all SDKs. @@ -161,7 +168,7 @@ tree as well as the unit of reporting to Sentry. ## Sampling -Each transaction has a "sampling decision," that is, a boolean which dictates whether or not it should be sent to Sentry. This should be set exactly once during a transaction's lifetime, and should be stored in an internal `sampled` boolean. +Each transaction has a _sampling decision_, that is, a boolean which declares whether or not it should be sent to Sentry. This should be set exactly once during a transaction's lifetime, and should be stored in an internal `sampled` boolean. There are multiple ways a transaction can end up with a sampling decision: @@ -171,7 +178,7 @@ There are multiple ways a transaction can end up with a sampling decision: - If the transaction has a parent, inheriting its parent's sampling decision - Absolute decision passed to `startTransaction` -When there's the potential for more than one of these to come into play, the following precedence rules should apply: +When there's the potential for more than one of these to apply, the following precedence rules should apply: 1. If a sampling decision is passed to `startTransaction` (`startTransaction({name: "my transaction", sampled: true})`), that decision will be used, regardlesss of anything else 2. If `tracesSampler` is defined, its decision will be used. It can choose to keep or ignore any parent sampling decision, or use the sampling context data to make its own decision or choose a sample rate for the transaction. @@ -201,25 +208,34 @@ Depending on the platform, other default data may be included. (For example, for A transaction's sampling decision should be passed to all of its children, including across service boundaries. This can be accomplished in the `startChild` method for same-service children and using the `senry-trace` header for children in a different service. -### Propagated Sampling Rates +### Propagated Random Value + +To increase the chance of capturing complete traces when users return a new sample rate from the `tracesSampler` in backend services, we propagate the generated random value used by the SDK for computing the sampling decision instead of sampling with a different random value in every service. Therefore, across a trace every SDK uses the same random value. -To increase the chance of capturing complete traces when users return a new sample rate from the `tracesSampler` in backend services, we propagate the generated random value used by the SDK for computing the sampling decision instead of creating a new random value in every service. Therefore, across a trace every SDK uses the same random value. +Without a `tracesSampler` callback, an SDK fully inherits sampling decisions for propagated traces. In this case, the presence of `sample_rand` in the DSC has no effect on the sampling decision. However, this behavior is subject to change in the future. -The behavior of the static `tracesSampleRate` without the use of `tracesSampler` does not change. We continue to fully inherit sampling decisions for propagated traces and create a new one for started traces. In the future, we might change the default behavior of `tracesSampleRate`, too. +The random value is set according to the following rules: -Once the SDK starts a new trace, `sentry-sample_rand` is filled with a truly random number. This also applies when the `tracesSampleRate` is set to `1.0`. If the SDK receives an incoming trace, containing a `sentry-sample_rand` entry in the baggage, this value should overwrite the currently stored number. For incoming traces without a `sentry-sample_rand` baggage entry (from old SDKs), the SDK inserts a new truly random number on the fly. +1. When an SDK starts a new trace, `sample_rand` is always set to a truly random number in range `[0, 1]`. This explicitly includes traces that are not sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. +2. It is _recommended_ to generate the random number deterministically using the trace ID as seed or source of randomness. The exact method by which the random number is created is implementation defined and may vary between SDK implementations. +3. On incoming traces, an SDK assumes the `sample_rand` value along with the rest of the DSC, overriding an existing value if needed. +4. If `sample_rand` is missing on an incoming trace, the SDK creates and from now on propagates a new random number on-the-fly, based on the following rules: + 1. If `sample_rate` and `sampled` are propgated, create `sample_rand` so that it adheres to the invariant. This means, for a decision of `True` generate a random number in half-open range `[0, rate)` and for a decision of `False` generate a random number in range `[rate, 1]`. + 2. If only `sampled` is propagated, apply the same strategy above using the output of the `tracesSampler` + `tracesSampleRate` cascade as cutoff point for the intervals. + 3. If the `sampled` is missing `sample_rate`, generate a random number in the range `[0, 1]` like for a new trace. -The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base. +The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base: -- When the `tracesSampler` is invoked, this also applies to the return value of traces sampler. i.e. `trace["sentry-sample_rand"] < tracesSampler(context)` -- Otherwise, when the SDK is the head of a trace, this applies to sample decisions based on `tracesSampleRate` , i.e. `trace["sentry-sample_rand"] < config.tracesSampleRate` +1. When the `tracesSampler` is invoked, this also applies to the return value of traces sampler. That is, `trace["sentry-sample_rand"] < tracesSampler(context)` +2. Otherwise, when the SDK is the head of a trace, this also applies to sample decisions based on `tracesSampleRate`. That is, `trace["sentry-sample_rand"] < config.tracesSampleRate` +3. There is no more direct comparison with `math.random()` during the sampling process. -When using a `tracesSampler`, the correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, we can still extrapolate counts correctly. +When using a `tracesSampler`, the correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, Sentry can still extrapolate counts correctly. ```js tracesSampler: ({ name, parentSampleRate }) => { - // Inherit the trace parent's sample rate if there is one. Sampling is deterministic - // for one trace, i.e. if the parent was sampled, we will be sampled too at the same + // Inherit the trace parent's sample rate if there is one. Sampling is deterministic + // for one trace, i.e. if the parent was sampled, we will be sampled too at the same // rate. if (typeof parentSampleRate === "number") { return parentSampleRate; From 2f00a67dac12fe776fa8ffb55dbeefaa50bdc068 Mon Sep 17 00:00:00 2001 From: Michi Hoffmann Date: Tue, 26 Nov 2024 17:00:43 +0100 Subject: [PATCH 06/12] Apply suggestions from code review Co-authored-by: Anton Pirker --- develop-docs/sdk/telemetry/traces/index.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index b3d3dca4ae10a..92f7fecc51f56 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -74,7 +74,7 @@ This Option replaces the non-standardized `tracingOrigins` option which was prev ### `strictTracePropagation` -This option disables trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. +This should be a boolean value. Default is `false`. This option controls trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. If the SDK can parse an org ID from the configured DSN, it must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. @@ -216,7 +216,7 @@ Without a `tracesSampler` callback, an SDK fully inherits sampling decisions for The random value is set according to the following rules: -1. When an SDK starts a new trace, `sample_rand` is always set to a truly random number in range `[0, 1]`. This explicitly includes traces that are not sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. +1. When an SDK starts a new trace, `sample_rand` is always set to a random number in range `[0, 1]`. This explicitly includes traces that are not sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. 2. It is _recommended_ to generate the random number deterministically using the trace ID as seed or source of randomness. The exact method by which the random number is created is implementation defined and may vary between SDK implementations. 3. On incoming traces, an SDK assumes the `sample_rand` value along with the rest of the DSC, overriding an existing value if needed. 4. If `sample_rand` is missing on an incoming trace, the SDK creates and from now on propagates a new random number on-the-fly, based on the following rules: From 52f334368c9556c14e4a422cd3c836d65ad8acea Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Tue, 26 Nov 2024 19:35:48 +0100 Subject: [PATCH 07/12] Cleanup --- develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx index 2c65b7a7f494d..bb233fe20900b 100644 --- a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx +++ b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx @@ -57,7 +57,7 @@ Additional entries such as `replay_id`, `org` and `sample_rand` are only using t | `trace_id` | string | The original trace ID as generated by the SDK. This must match the trace id of the submitted transaction item. [1] | `771a43a4192642f0b136d5159a501700` | strictly required [0] | | `public_key` | string | Public key from the DSN used by the SDK. [2] | `49d0f7386ad645858ae85020e393bef3` | strictly required [0] | | `sample_rate` | string | The sample rate as defined by the user on the SDK. [3] [4] | `0.7` | strictly required [0] | -| `sample_rand` | string | A truly random number originating from the head of trace SDK. [4] | `0.5` | required | +| `sample_rand` | string | A random number originating from the head of trace SDK. [4] | `0.5` | required | | `sampled` | string | `"true"` if the trace is sampled, `"false"` otherwise. This is set by the head of the trace. [4] | `true` | required | | `release` | string | The release name as specified in client options. | `myapp@1.2.3`, `1.2.3`, `2025.4.107` | required | | `environment` | string | The environment name as specified in client options. | `production`, `staging` | required | From 420d6524714135322d584af275e43b6ab9237e66 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Tue, 26 Nov 2024 19:42:13 +0100 Subject: [PATCH 08/12] Add new strictTracePropagation behaviour --- develop-docs/sdk/telemetry/traces/index.mdx | 28 ++++++++++++++++----- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index 92f7fecc51f56..bf4662e9b2047 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -74,18 +74,34 @@ This Option replaces the non-standardized `tracingOrigins` option which was prev ### `strictTracePropagation` -This should be a boolean value. Default is `false`. This option controls trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. +This must be a boolean value. Default is `false`. This option controls trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. -If the SDK can parse an org ID from the configured DSN, it must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. +If the SDK is able parse an org ID from the configured DSN, it must be propagated as a baggage entry with the key `sentry-org`. Given a DSN of `https://1234@o1.ingest.us.sentry.io/1`, the org ID is `1`, based on `o1`. -On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN. Only if both match, the trace is continued. If there is no match, neither the trace ID, the parent sampling decision nor the baggage should be taken into account. -The SDK should behave like it is the head of trace in this case. +Addiotnally, the SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. This option should be set when running a self-hosted version of Sentry or if a non-standard Sentry DSN is used, such as when using a local Relay. + +On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN or org setting. Only if both match, the trace is continued. If there is no match, neither the trace ID, the parent sampling decision nor the baggage should be taken into account. +The SDK should behave like it is the head of trace in this case, and not consider any propagted values. This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. -Initially, SDKs should introduce the this option with a default value of `false`. +Initially, SDKs must introduce the this option with a default value of `false`. Once the majority of SDKs introduced this option, we will change the default value to `true` (in a major version bump) making it opt-out. -The SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. This option should be set when running a self-hosted version of Sentry or if a non-standard Sentry DSN is used, such as when using a local Relay. +Regardless of `strictTracePropagation` being set to `true` or `false`, if the SDK is either configured with a `org` or was able to parse the value from the DSN, incoming traces containing an `org` value in the baggage that does not match the one from the receiving SDK, the trace is not continuned. + +Examples: + +baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: false` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: false` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: false` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: false` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: false` -> drop trace + +baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: true` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: true` -> drop trace +baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: true` -> drop trace +baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: true` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: true` -> drop trace ### `traceOptionsRequests` From 4171e84d322f901e8340e9d4ab5da005e3f12c88 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Tue, 26 Nov 2024 19:46:14 +0100 Subject: [PATCH 09/12] Cleanup --- develop-docs/sdk/telemetry/traces/index.mdx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index bf4662e9b2047..a52b406c9e4b0 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -233,12 +233,11 @@ Without a `tracesSampler` callback, an SDK fully inherits sampling decisions for The random value is set according to the following rules: 1. When an SDK starts a new trace, `sample_rand` is always set to a random number in range `[0, 1]`. This explicitly includes traces that are not sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. -2. It is _recommended_ to generate the random number deterministically using the trace ID as seed or source of randomness. The exact method by which the random number is created is implementation defined and may vary between SDK implementations. +2. It is _recommended_ to generate the random number deterministically using the trace ID as seed or source of randomness. The exact method by which the random number is created is implementation defined and may vary between SDK implementations. See 4. on why this behaviour is desirable. 3. On incoming traces, an SDK assumes the `sample_rand` value along with the rest of the DSC, overriding an existing value if needed. 4. If `sample_rand` is missing on an incoming trace, the SDK creates and from now on propagates a new random number on-the-fly, based on the following rules: 1. If `sample_rate` and `sampled` are propgated, create `sample_rand` so that it adheres to the invariant. This means, for a decision of `True` generate a random number in half-open range `[0, rate)` and for a decision of `False` generate a random number in range `[rate, 1]`. - 2. If only `sampled` is propagated, apply the same strategy above using the output of the `tracesSampler` + `tracesSampleRate` cascade as cutoff point for the intervals. - 3. If the `sampled` is missing `sample_rate`, generate a random number in the range `[0, 1]` like for a new trace. + 2. If the `sampled` is missing `sample_rate`, generate a random number in the range `[0, 1]` like for a new trace. The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base: From 44903cbf6efeadbe3bec52767441b6e3286f15ba Mon Sep 17 00:00:00 2001 From: Michi Hoffmann Date: Tue, 26 Nov 2024 19:55:42 +0100 Subject: [PATCH 10/12] Apply suggestions from code review Co-authored-by: Liza Mock --- develop-docs/sdk/telemetry/traces/index.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index a52b406c9e4b0..2da49a9b68860 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -16,7 +16,7 @@ Reference implementations: -This document uses standard interval notation, where `[` and `]` denote closed intervals which include the endpoints of the interval, while `(` and `)` denote open intervals which exclude the endpoints of the interval. An interval `[x, y)` covers all values starting from `x` up to but excluding `y`. +This document uses standard interval notation, where `[` and `]` indicates closed intervals, which include the endpoints of the interval, while `(` and `)` indicates open intervals, which exclude the endpoints of the interval. An interval `[x, y)` covers all values starting from `x` up to but excluding `y`. @@ -32,13 +32,13 @@ This option is **deprecated** and should be removed from all SDKs. ### `tracesSampleRate` -This should be a float point number in the range `[0, 1]` and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](#sampling), `0.0` is a guaranteed 0% chance (none will be sent) and `1.0` is a guaranteed 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction should have the same random chance of ending up with `sampled = true`, equal to the `tracesSampleRate`. +This should be a floating-point number in the range `[0, 1]` and represents the percentage chance that any given transaction will be sent to Sentry. So, barring [outside influence](#sampling), `0.0` is a guaranteed 0% chance (none will be sent) and `1.0` is a guaranteed 100% chance (all will be sent). This rate applies equally to all transactions; in other words, each transaction has an equal chance of being marked as `sampled = true`, based on the `tracesSampleRate`. See more about how sampling should be performed [below](#sampling). ### `tracesSampler` -This should be a callback, called when a transaction is started, which will be given a `samplingContext` object and which should return a sample rate in range `[0, 1]` _for the transaction in question_. This sample rate should behave the same way as the `tracesSampleRate` above, with the difference that it only applies to the newly-created transaction, such that different transactions can be sampled at different rates. Returning `0.0` should force the transaction to be dropped (set to `sampled = false`) and returning `1.0` should force the transaction to be sent (set `sampled = true`). +This should be a callback function, triggered when a transaction is started. It should be given a `samplingContext` object and should return a sample rate in the range of `[0, 1]` _for the transaction in question_. This sample rate should behave the same way as the `tracesSampleRate` above. The only difference is that it only applies to the newly-created transaction and that different transactions can be sampled at different rates. Returning `0.0` should force the transaction to be dropped (set to `sampled = false`) and returning `1.0` should force the transaction to be sent (set to `sampled = true`). Historically, the `tracesSampler` callback could have also returned a boolean to force a sampling decision (with `false` equivalent to `0.0` and `true` equivalent to `1.0`). This behavior is now **deprecated** and should be removed from all SDKs. @@ -194,7 +194,7 @@ There are multiple ways a transaction can end up with a sampling decision: - If the transaction has a parent, inheriting its parent's sampling decision - Absolute decision passed to `startTransaction` -When there's the potential for more than one of these to apply, the following precedence rules should apply: +If more than one option could apply, the following rules determine which takes precedence: 1. If a sampling decision is passed to `startTransaction` (`startTransaction({name: "my transaction", sampled: true})`), that decision will be used, regardlesss of anything else 2. If `tracesSampler` is defined, its decision will be used. It can choose to keep or ignore any parent sampling decision, or use the sampling context data to make its own decision or choose a sample rate for the transaction. From 7364dfbc4a3a0e097972627b1b08e83b25946e18 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Wed, 27 Nov 2024 12:24:39 +0100 Subject: [PATCH 11/12] Wording --- .../sdk/telemetry/traces/dynamic-sampling-context.mdx | 4 ++-- develop-docs/sdk/telemetry/traces/index.mdx | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx index bb233fe20900b..540ed01c3f9af 100644 --- a/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx +++ b/develop-docs/sdk/telemetry/traces/dynamic-sampling-context.mdx @@ -57,8 +57,8 @@ Additional entries such as `replay_id`, `org` and `sample_rand` are only using t | `trace_id` | string | The original trace ID as generated by the SDK. This must match the trace id of the submitted transaction item. [1] | `771a43a4192642f0b136d5159a501700` | strictly required [0] | | `public_key` | string | Public key from the DSN used by the SDK. [2] | `49d0f7386ad645858ae85020e393bef3` | strictly required [0] | | `sample_rate` | string | The sample rate as defined by the user on the SDK. [3] [4] | `0.7` | strictly required [0] | -| `sample_rand` | string | A random number originating from the head of trace SDK. [4] | `0.5` | required | -| `sampled` | string | `"true"` if the trace is sampled, `"false"` otherwise. This is set by the head of the trace. [4] | `true` | required | +| `sample_rand` | string | A random number generated at the start of a trace by the head of trace SDK. [4] | `0.5` | required | +| `sampled` | string | `"true"` if the trace is sampled, `"false"` otherwise. This is set by the head of the trace SDK. [4] | `true` | required | | `release` | string | The release name as specified in client options. | `myapp@1.2.3`, `1.2.3`, `2025.4.107` | required | | `environment` | string | The environment name as specified in client options. | `production`, `staging` | required | | `transaction` | string | The transaction name set on the scope. **Only include** if name has [good quality](#note-on-good-quality-transaction-names). | `/login`, `myApp.myController.login` | required (if known and good quality) | diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index 2da49a9b68860..ceba44435af59 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -85,7 +85,7 @@ The SDK should behave like it is the head of trace in this case, and not conside This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. Initially, SDKs must introduce the this option with a default value of `false`. -Once the majority of SDKs introduced this option, we will change the default value to `true` (in a major version bump) making it opt-out. +Once the majority of SDKs have introduced this option, we'll change the default value to `true` (in a major version bump), making it opt-out. Regardless of `strictTracePropagation` being set to `true` or `false`, if the SDK is either configured with a `org` or was able to parse the value from the DSN, incoming traces containing an `org` value in the baggage that does not match the one from the receiving SDK, the trace is not continuned. @@ -95,13 +95,13 @@ baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: false` -> baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: false` -> continue trace baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: false` -> continue trace baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: false` -> continue trace -baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: false` -> drop trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: false` -> start new trace baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: true` -> continue trace -baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: true` -> drop trace -baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: true` -> drop trace +baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: true` -> start new trace +baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: true` -> start new trace baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: true` -> continue trace -baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: true` -> drop trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: true` -> start new trace ### `traceOptionsRequests` From e4d90543229127e345f763f9ae5d1f605ae4e693 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Thu, 28 Nov 2024 11:42:34 +0100 Subject: [PATCH 12/12] Wording --- develop-docs/sdk/telemetry/traces/index.mdx | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/develop-docs/sdk/telemetry/traces/index.mdx b/develop-docs/sdk/telemetry/traces/index.mdx index ceba44435af59..516872fc0de6b 100644 --- a/develop-docs/sdk/telemetry/traces/index.mdx +++ b/develop-docs/sdk/telemetry/traces/index.mdx @@ -72,7 +72,7 @@ This Option replaces the non-standardized `tracingOrigins` option which was prev -### `strictTracePropagation` +### `strictTraceContinuation` This must be a boolean value. Default is `false`. This option controls trace continuation from unknown 3rd party services that happen to be instrumented by a Sentry SDK. @@ -83,25 +83,25 @@ Addiotnally, the SDK must be configurable with an optional `org: ` setti On incoming traces, the SDK must compare the `sentry-org` baggage value against its own parsed value from the DSN or org setting. Only if both match, the trace is continued. If there is no match, neither the trace ID, the parent sampling decision nor the baggage should be taken into account. The SDK should behave like it is the head of trace in this case, and not consider any propagted values. -This behavior can be disabled by setting `strictTracePropagation: false` in the SDK init call. +This behavior can be disabled by setting `strictTraceContinuation: false` in the SDK init call. Initially, SDKs must introduce the this option with a default value of `false`. Once the majority of SDKs have introduced this option, we'll change the default value to `true` (in a major version bump), making it opt-out. -Regardless of `strictTracePropagation` being set to `true` or `false`, if the SDK is either configured with a `org` or was able to parse the value from the DSN, incoming traces containing an `org` value in the baggage that does not match the one from the receiving SDK, the trace is not continuned. +Regardless of `strictTraceContinuation` being set to `true` or `false`, if the SDK is either configured with a `org` or was able to parse the value from the DSN, incoming traces containing an `org` value in the baggage that does not match the one from the receiving SDK, the trace is not continuned. Examples: -baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: false` -> continue trace -baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: false` -> continue trace -baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: false` -> continue trace -baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: false` -> continue trace -baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: false` -> start new trace +baggage: `sentry-org: 1`, SDK config: `org: 1, strictTraceContinuation: false` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: 1, strictTraceContinuation: false` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: none, strictTraceContinuation: false` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: none, strictTraceContinuation: false` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTraceContinuation: false` -> start new trace -baggage: `sentry-org: 1`, SDK config: `org: 1, strictTracePropagation: true` -> continue trace -baggage: `sentry-org: none`, SDK config: `org: 1, strictTracePropagation: true` -> start new trace -baggage: `sentry-org: 1`, SDK config: `org: none, strictTracePropagation: true` -> start new trace -baggage: `sentry-org: none`, SDK config: `org: none, strictTracePropagation: true` -> continue trace -baggage: `sentry-org: 1`, SDK config: `org: 2, strictTracePropagation: true` -> start new trace +baggage: `sentry-org: 1`, SDK config: `org: 1, strictTraceContinuation: true` -> continue trace +baggage: `sentry-org: none`, SDK config: `org: 1, strictTraceContinuation: true` -> start new trace +baggage: `sentry-org: 1`, SDK config: `org: none, strictTraceContinuation: true` -> start new trace +baggage: `sentry-org: none`, SDK config: `org: none, strictTraceContinuation: true` -> continue trace +baggage: `sentry-org: 1`, SDK config: `org: 2, strictTraceContinuation: true` -> start new trace ### `traceOptionsRequests` @@ -226,26 +226,26 @@ A transaction's sampling decision should be passed to all of its children, inclu ### Propagated Random Value -To increase the chance of capturing complete traces when users return a new sample rate from the `tracesSampler` in backend services, we propagate the generated random value used by the SDK for computing the sampling decision instead of sampling with a different random value in every service. Therefore, across a trace every SDK uses the same random value. +To improve the likelihood of capturing complete traces when backend services use a custom sample rate via `tracesSampler`, the SDK propagates the same random value used for sampling decisions across all services in a trace. This ensures consistent sampling decisions across a trace instead of generating a new random value for each service. -Without a `tracesSampler` callback, an SDK fully inherits sampling decisions for propagated traces. In this case, the presence of `sample_rand` in the DSC has no effect on the sampling decision. However, this behavior is subject to change in the future. +If no `tracesSampler` callback is used, the SDK fully inherits sampling decisions for propagated traces, and the presence of `sample_rand` in the DSC doesn't affect the decision. However, this behavior may change in the future. The random value is set according to the following rules: -1. When an SDK starts a new trace, `sample_rand` is always set to a random number in range `[0, 1]`. This explicitly includes traces that are not sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. +1. When an SDK starts a new trace, `sample_rand` is always set to a random number in the range of `[0, 1]`. This explicitly includes traces that aren't sampled, as well as when the `tracesSampleRate` is set to `0.0` or `1.0`. 2. It is _recommended_ to generate the random number deterministically using the trace ID as seed or source of randomness. The exact method by which the random number is created is implementation defined and may vary between SDK implementations. See 4. on why this behaviour is desirable. 3. On incoming traces, an SDK assumes the `sample_rand` value along with the rest of the DSC, overriding an existing value if needed. 4. If `sample_rand` is missing on an incoming trace, the SDK creates and from now on propagates a new random number on-the-fly, based on the following rules: 1. If `sample_rate` and `sampled` are propgated, create `sample_rand` so that it adheres to the invariant. This means, for a decision of `True` generate a random number in half-open range `[0, rate)` and for a decision of `False` generate a random number in range `[rate, 1]`. - 2. If the `sampled` is missing `sample_rate`, generate a random number in the range `[0, 1]` like for a new trace. + 2. If the sampling decision is missing, generate a random number in range of `[0, 1]`, like for a new trace. -The SDK should exclusively use the stored random number and no longer use `math.random()` or similar anywhere else in the tracing related code base: +The SDK should always use the stored random number (`sentry-sample_rand`) for sampling decisions and should no longer rely on `math.random()` or similar functions in tracing code: 1. When the `tracesSampler` is invoked, this also applies to the return value of traces sampler. That is, `trace["sentry-sample_rand"] < tracesSampler(context)` 2. Otherwise, when the SDK is the head of a trace, this also applies to sample decisions based on `tracesSampleRate`. That is, `trace["sentry-sample_rand"] < config.tracesSampleRate` 3. There is no more direct comparison with `math.random()` during the sampling process. -When using a `tracesSampler`, the correct way to inherit parent sampling decisions is now to return the parent's sample rate instead of the decision as float (1.0). This way, Sentry can still extrapolate counts correctly. +When using a `tracesSampler`, the proper way to inherit a parent's sampling decision is to return the parent's sample rate, instead of leaving the decision as a float (for example, 1.0). This way, Sentry can still extrapolate counts correctly. ```js tracesSampler: ({ name, parentSampleRate }) => {