From dc919c014c0c1f598d81cb450883ddc4866255ed Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Tue, 5 Nov 2024 08:37:13 +0100 Subject: [PATCH 01/13] Add skeleton --- develop-docs/sdk/telemetry/spans/index.mdx | 13 +++++++++++++ develop-docs/sdk/telemetry/spans/scopes.mdx | 0 develop-docs/sdk/telemetry/spans/span-api.mdx | 7 +++++++ .../sdk/telemetry/spans/span-properties.mdx | 7 +++++++ develop-docs/sdk/telemetry/spans/span-protocol.mdx | 7 +++++++ develop-docs/sdk/telemetry/spans/span-sampling.mdx | 7 +++++++ 6 files changed, 41 insertions(+) create mode 100644 develop-docs/sdk/telemetry/spans/index.mdx create mode 100644 develop-docs/sdk/telemetry/spans/scopes.mdx create mode 100644 develop-docs/sdk/telemetry/spans/span-api.mdx create mode 100644 develop-docs/sdk/telemetry/spans/span-properties.mdx create mode 100644 develop-docs/sdk/telemetry/spans/span-protocol.mdx create mode 100644 develop-docs/sdk/telemetry/spans/span-sampling.mdx diff --git a/develop-docs/sdk/telemetry/spans/index.mdx b/develop-docs/sdk/telemetry/spans/index.mdx new file mode 100644 index 0000000000000..fd9551aa6b06d --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/index.mdx @@ -0,0 +1,13 @@ +--- +title: Spans +sidebar_order: 8 +--- + +This section covers how SDKs should implement a spans-only API to support Sentry's tracing product. + +Span API +Span Properties +Span Sampling +Span Protocol + +Scopes diff --git a/develop-docs/sdk/telemetry/spans/scopes.mdx b/develop-docs/sdk/telemetry/spans/scopes.mdx new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/develop-docs/sdk/telemetry/spans/span-api.mdx b/develop-docs/sdk/telemetry/spans/span-api.mdx new file mode 100644 index 0000000000000..9ad7365450e94 --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/span-api.mdx @@ -0,0 +1,7 @@ +--- +title: Span API +--- + + + + diff --git a/develop-docs/sdk/telemetry/spans/span-properties.mdx b/develop-docs/sdk/telemetry/spans/span-properties.mdx new file mode 100644 index 0000000000000..bf1e5df745442 --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/span-properties.mdx @@ -0,0 +1,7 @@ +--- +title: Span Propertires +--- + + + + diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx new file mode 100644 index 0000000000000..22a8cd3adf99b --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -0,0 +1,7 @@ +--- +title: Span Protocol +--- + + + + diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx new file mode 100644 index 0000000000000..147212ae0afc6 --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -0,0 +1,7 @@ +--- +title: Span Sampling +--- + + + + From 83a4125099f2adafab2d7aee6e4e3bc9752e2ce7 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Tue, 5 Nov 2024 09:18:05 +0100 Subject: [PATCH 02/13] Add current state of sub pages --- develop-docs/sdk/telemetry/spans/index.mdx | 9 +- develop-docs/sdk/telemetry/spans/span-api.mdx | 176 ++++++++++++++++++ .../sdk/telemetry/spans/span-properties.mdx | 8 + .../sdk/telemetry/spans/span-protocol.mdx | 58 +++++- .../sdk/telemetry/spans/span-sampling.mdx | 143 ++++++++++++++ 5 files changed, 385 insertions(+), 9 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/index.mdx b/develop-docs/sdk/telemetry/spans/index.mdx index fd9551aa6b06d..6853b90e443fd 100644 --- a/develop-docs/sdk/telemetry/spans/index.mdx +++ b/develop-docs/sdk/telemetry/spans/index.mdx @@ -3,11 +3,4 @@ title: Spans sidebar_order: 8 --- -This section covers how SDKs should implement a spans-only API to support Sentry's tracing product. - -Span API -Span Properties -Span Sampling -Span Protocol - -Scopes + diff --git a/develop-docs/sdk/telemetry/spans/span-api.mdx b/develop-docs/sdk/telemetry/spans/span-api.mdx index 9ad7365450e94..4adb09fd9a714 100644 --- a/develop-docs/sdk/telemetry/spans/span-api.mdx +++ b/develop-docs/sdk/telemetry/spans/span-api.mdx @@ -2,6 +2,182 @@ title: Span API --- +# Span API +### `startSpan` +Calling the global `startSpan` method always creates a root span. +```jsx +span = sentry.tracing.startSpan({ + name: 'GET /', + op: 'http.server' +}) +``` + +```jsx +span = sentry().tracing().startSpan() + ->setName('GET /') + ->setOp('http.server') +``` + +### `end` + +This method sets the end timestamp on the span. If the span is a root span, it emits the entire span tree to Sentry. + +```jsx +span = sentry.tracing.startSpan({ + name: 'GET /', + op: 'http.server' +}) + +span.end() +``` + +```jsx +span = sentry().tracing().startSpan() + ->setName('GET /') + ->setOp('http.server') + +span.end() +``` + +### `continueTraceFrom` + +Creates a new root span which continues the trace based on the provided trace and parent span id. + +```jsx +span = sentry.tracing.startSpan({ + traceId: '', + parentSpanId: '', + sampled: true, + name: 'GET /' + op: 'http.server' +}) +``` + +```jsx +span = sentry.tracing.startSpan({ + continueTraceFrom: span, + name: 'GET /' + op: 'http.server' +}) +``` + +```jsx +span = sentry().tracing().continueTraceFrom({ + traceId: '', + parentSpanId: '', + sampled: true, + }) + ->setName('GET /') + ->setOp('http.server') +``` + +```jsx +span = sentry().tracing().continueTraceFrom(span) + ->setName('GET /') + ->setOp('http.server') +``` + +### Start a new root span + +Calling the global `startSpan` method always creates a root span. + +```jsx +span = sentry.tracing.startSpan({ + name: 'GET /', + op: 'http.server', +}) +``` + +```jsx +span = sentry().tracing().startSpan() + ->setName('GET /') + ->setOp('http.server') +``` + +### `setAttribute` + +```jsx +span = sentry.tracing.startSpan({ + name: 'GET /', + op: 'http.server', + attributes: { + attr1: "value1", + attr2: 42, + attr3: true, + }, +}) +``` + +```jsx +span = sentry().tracing().startSpan() + ->setName('GET /') + ->setOp('http.server') + ->setAtttribute( + key: 'attr1', + value: 'value1', + ) +``` + +### Starting a child span + +To create nested spans, you must call `startSpan` on the parent span. + +```jsx +span = sentry.tracing.startSpan({ ... }) +childSpan = span.startSpan({ ... }) +``` + +```jsx +span = sentry().tracing().startSpan() +childSpan = span.startSpan() +``` + +### Get the current span + +```jsx +span = scope.getSpan() +``` + +### Set the current span + +```jsx +scope.setSpan(span) +``` + +### Get the root span + +```jsx +rootSpan = scope.getRootSpan() +``` + +### Additional APIs + +span.`setStartTimestamp()` - overwrite the span’s start time + +span.`setEndTimestamp()` - overwrites the span’s end time + +span.`setStatus()` - success, error, unknown + +span.`getBaggage()` - returns the baggage string + +span.`getTraceparent()` - returns the sentry trace parent string + +### Examples + +```jsx +rootSpan = sentry.tracing.startSpan({ + name: 'GET /', + op: 'http.server', +}) + +span = rootSpan.startSpan({ + name: 'middleware.handle', + op: 'http.middleware', +}) + +span.end() +rootSpan.end() +``` diff --git a/develop-docs/sdk/telemetry/spans/span-properties.mdx b/develop-docs/sdk/telemetry/spans/span-properties.mdx index bf1e5df745442..d472aff3321d2 100644 --- a/develop-docs/sdk/telemetry/spans/span-properties.mdx +++ b/develop-docs/sdk/telemetry/spans/span-properties.mdx @@ -2,6 +2,14 @@ title: Span Propertires --- +# Span Propertires +Instead of spans containing tags, context, and data, we'll unify all these properties into a new “attributes” property. +Similar to OTel's semantic conventions, we'll add special meaning to certain attribute keys, such as `sentry.release`, `sentry.op`, etc. +```js +span.setAttribute('http.request.method', 'GET') +span.setAttribute('user.email', 'jane.doe@example.com') +``` +We'll map these attributes to their respective existing property in Relay to ease the work required for the product during the transition period. diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx index 22a8cd3adf99b..9c28c0480fb5e 100644 --- a/develop-docs/sdk/telemetry/spans/span-protocol.mdx +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -2,6 +2,62 @@ title: Span Protocol --- +# Span protocol +We'll introduce a new “span” envelope item, which the SDK uses to emit a segment span and its children. And in the future, a batch of spans. +The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/), which introduced typed attributes and will ease the conversion in our POtel SDKs. - +```json +{ + "event_id":"9ec79c33ec9942ab8353589fcb2e04dc" +} +{ + "type": "span" +} +{ + "traceId": "32d3c7cb501fbddbe3ce1016a72d50b5", + "spanId": "e91d37480970523b", + "name": "GET /", + "startTime": "1544712660", + "endTime": "1544712680", + "attributes": [ + { + "key": "sentry.op", + "value": { + "stringValue": "http.sever", + } + }, + { + "key": "http.response.status_code", + "value": { + "intValue": "200", + } + } + } +} +{ + "type": "span" +} +{ + "traceId": "32d3c7cb501fbddbe3ce1016a72d50b5", + "spanId": "6b22b3af586e777a", + "parentSpanId": "e91d37480970523b", + "name": "UserMiddleware", + "startTimeUnix": "1544712665", + "endTimeUnix": "1544712675", + "attributes": [ + { + "key": "sentry.op", + "value": { + "stringValue": "middleware.handle", + } + }, + { + "key": "user.email", + "value": { + "stringValue": "jane.doe@example.com", + } + } + } +} +``` diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index 147212ae0afc6..1725ea9f2d917 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -2,6 +2,149 @@ title: Span Sampling --- +# Span Sampling +With the metrics product shifting to a sampling based solution, extrapolation is of the utter most importance to being able to display reliable metrics to our users. We want to account for client sampling in addition to server sampling. +This requires the SDKs to always report the correct sampling rates in each tracing related envelope send to Sentry. +Directional, the goal is to create complete traces by default and wherever possible. We will not optimise for spent-control. +We historically exposed many ways to our users to remove certain transactions or spans from being emitted to Sentry. This resulted in convoluted SDK APIs, weird edge cases in the product and an overall bad user experience. More importantly, these sampling controls will contribute to vastly wrong metrics being extracted from span attributes, hence we need to rework those: +- `beforeSendTransaction` and `beforeSendSpan` will be replaced with `beforeSendSpans`, which encourages users to *mutate* spans, but they cannot be dropped through this callback. +- All SDK integrations that create spans, need to be able to be turned off via a config flag for the purpose of noise reduction or via a new `ignoreSpans` options that accepts a glob pattern. +- Sampling happens exclusively via `tracesSampleRate` or `tracesSampler`. We need to make sure to always prefer the parent sampling decision, either via explicit docs or a new argument for the `tracesSampler` or SDK option. +- Trace propagation is aware of applications or at least organizations and prevents “leaking” traces across this boundary. + +## `beforeSendSpans` + +The primary use-case for this hook will be data scrubbing or mutating certain properties of spans. + +We are likely only allow to mutate the span’s name, timestamps, status and most attributes. Trace ID, span ID, parent span ID are immutable, as well as certain span attributes, such as segment ID. + +It is yet to be defined which arguments will be passed into the callback or how the hook behaves with transaction envelopes. + +## Span configuration + +To reduce noise, users might want to disable certain integrations creating spans. This should ideally be exposed as a global config or at an integration level. Additionally, a new `ignoreSpans` option will allow users to not emit certain spans based on their name & attributes. + +```jsx +Sentry.init({ + dsn: 'foo@bar', + ignoreSpans: [ + 'GET /about', + 'events.signal *', + ], + ignoreSpans: (name, attributes) { + if ( + name === 'server.request' && + attributes['server.address'] === 'https://sentry.io' + ) { + return true + } + }, + integrations: [ + fsIntegration: { + ignoreSpans: [ + 'fs.read', + ], + readSpans: true, + writeSpans: false, + } + ] +}) +``` + +## Parent Sampling Decision + +In today's SDKs, a parent sampling decision received via a `sentry-trace` header or similar can be overruled by setting a `tracesSampler`. +As we need to optimize for trace completeness, we need to explicitly call out the impact of the sampler or change the behaviour to always use the parent’s decision unless explicitly opted-out. + +```jsx +// Explict docs +Sentry.init({ + tracesSampler: ({ name, attributes, parentSampled }) => { + // Continue trace decision, if there is any parentSampled information + // This is crucial for complete traces + if (typeof parentSampled === "boolean") { + return parentSampled; + } + + // Else, use default sample rate (replacing tracesSampleRate) + return 0.5; + }, +}) + +// Not chosen - New top level option +Sentry.init({ + ignoreParentSamplingDecision: true, + tracesSampler: ({ name, attributes, parentSampled }) => { + // Do not sample health checks ever + if (name.includes("healthcheck")) { + // Drop this transaction, by setting its sample rate to 0% + return 0.0; + } + + // Else, use default sample rate (replacing tracesSampleRate) + return 0.2; + }, +}) +``` + +## Parent Sampling Origins + +In order to filter out unrelated 3rd party services that are making requests to a Sentry instrumented app containing a `sentry-trace` header, we’ll implement RFC https://github.com/getsentry/rfcs/pull/137. This feature might be enabled by default if the: + +- SDK knows its org +- The incoming baggage header contains a `sentry-org` entry + +## Sampling Seed in DSC + +To increase the chance of capturing complete traces when users return a new sample rate `tracesSampler` in backend services, we propagate the 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. + +### Behavior + +A user can also override the parent sample rate in traces sampler. For example, a backend service has a `tracesSampler` that overrides frontend traces. This leads to three scenarios: + +- The new (backend) sample rate is lower than the parent’s (frontend): All traces captured in the backend are complete. There are additional partial traces for the frontend. +- The new (backend) sample rate is higher than the parent’s (fronted): All traces propagated from the frontend are complete. There are additional partial traces for the backend. +- Both sample rates are equal: All traces are complete, the sampling decision is fully inherited. + +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. + +### SDK Spec + +- sentry baggage gains a new field `sentry-sample_rand` + - when a new trace is started, `sentry-sample_rand` is filled with a truly random number. this also applies when the trace’s sample rate is 1.0 + - for inbound traces without a `sentry-sample_rand` (from old SDKs), the SDK inserts a new truly random number on-the-fly. +- sampling decisions in the SDK that currently compare `sentry-sample_rand` from the trace instead of `math.random()` with the sample rate. + - when traces sampler is invoked, this also applies to the return value of traces sampler. ie. `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` + - There is no more `math.random()` directly involved in any sampling decision. +- in traces sampler, 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. + + ```jsx + 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; + }, + + ``` + + - if the `sentry-sample_rate` (`parentSampleRate`) is not available for any reason for an inbound trace, but the trace has the sampled flag set to `true`, the SDK injects `parentSampleRate: 1.0` into the callback. + +## Baggage Freeze + +--- + +We accept partial traces under the assumption that the transaction name is mostly changed early in the request cycle. + +# External Resources + +https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/ From 8bb3a089196c7a9bc1f4de74b6cae8eed3c1c25b Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Thu, 7 Nov 2024 10:54:20 +0100 Subject: [PATCH 03/13] Update Span API --- develop-docs/sdk/telemetry/spans/span-api.mdx | 183 ++++-------------- 1 file changed, 37 insertions(+), 146 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/span-api.mdx b/develop-docs/sdk/telemetry/spans/span-api.mdx index 4adb09fd9a714..2e171c745e983 100644 --- a/develop-docs/sdk/telemetry/spans/span-api.mdx +++ b/develop-docs/sdk/telemetry/spans/span-api.mdx @@ -4,180 +4,71 @@ title: Span API # Span API -### `startSpan` +Spans are measuring the duration of certain operations in an application. +The topmost member of a span tree is called the root span. This span has no parent span and groups together its children with a representative name for the entire operation, such as `GET /` in case of a request to a backend application. -Calling the global `startSpan` method always creates a root span. +### Creating a root span -```jsx -span = sentry.tracing.startSpan({ - name: 'GET /', - op: 'http.server' -}) -``` +The SDK must expose a method for creating a root span. The user must be able to set certain properties on this root span, such as its name, the type of operation (`op`) and others. -```jsx -span = sentry().tracing().startSpan() +```js +span = sentry.tracing.startSpan() ->setName('GET /') ->setOp('http.server') -``` - -### `end` -This method sets the end timestamp on the span. If the span is a root span, it emits the entire span tree to Sentry. - -```jsx -span = sentry.tracing.startSpan({ - name: 'GET /', - op: 'http.server' -}) - -span.end() -``` - -```jsx -span = sentry().tracing().startSpan() - ->setName('GET /') - ->setOp('http.server') - span.end() ``` -### `continueTraceFrom` - -Creates a new root span which continues the trace based on the provided trace and parent span id. - -```jsx -span = sentry.tracing.startSpan({ - traceId: '', - parentSpanId: '', - sampled: true, - name: 'GET /' - op: 'http.server' -}) -``` - -```jsx -span = sentry.tracing.startSpan({ - continueTraceFrom: span, - name: 'GET /' - op: 'http.server' -}) -``` - -```jsx -span = sentry().tracing().continueTraceFrom({ - traceId: '', - parentSpanId: '', - sampled: true, - }) - ->setName('GET /') - ->setOp('http.server') -``` - -```jsx -span = sentry().tracing().continueTraceFrom(span) - ->setName('GET /') - ->setOp('http.server') -``` +### Creating nested spans -### Start a new root span +To create nested spans, the SDK must expose an explicit way for a user to perform this task. -Calling the global `startSpan` method always creates a root span. +Additionally, the SDK may expose alternative APIs to create nested spans, such as allowing a user to wrap an operation into a callback or apply a decorator to certain blocks. These alternative APIs must never create a root span and no-op if no parent span is present. -```jsx -span = sentry.tracing.startSpan({ - name: 'GET /', - op: 'http.server', -}) +```js +childSpan = span.startChild() + ->setName('authentication middleware') + ->setOp('middleware.handle') + +childSpan.end() ``` -```jsx -span = sentry().tracing().startSpan() - ->setName('GET /') - ->setOp('http.server') -``` +### Setting the span status -### `setAttribute` - -```jsx -span = sentry.tracing.startSpan({ - name: 'GET /', - op: 'http.server', - attributes: { - attr1: "value1", - attr2: 42, - attr3: true, - }, -}) -``` +A span has two statuses, `ok` and `error`. By default, the status of a span is set to `ok`. +The SDK must allow a user to modify the status of a span. -```jsx -span = sentry().tracing().startSpan() - ->setName('GET /') - ->setOp('http.server') - ->setAtttribute( - key: 'attr1', - value: 'value1', - ) +```js +span.setStatus('error') ``` -### Starting a child span +### Setting span attributes -To create nested spans, you must call `startSpan` on the parent span. +The SDK must expose a method to allow a user to set data attributes onto a span. +These attributes should use pre-defined keys whenever possible. -```jsx -span = sentry.tracing.startSpan({ ... }) -childSpan = span.startSpan({ ... }) +```js +span.setAttribute(SpanAttributes.HTTP_METHOD, 'GET') +span.setAttribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 200) ``` -```jsx -span = sentry().tracing().startSpan() -childSpan = span.startSpan() -``` +### Receiving the trace parent -### Get the current span +The SDK must expose a method to receive the baggage string. -```jsx -span = scope.getSpan() +```js +traceparent = span.getTraceparent() ``` -### Set the current span - -```jsx -scope.setSpan(span) -``` +### Receiving the baggage -### Get the root span +The SDK must expose a method to receive the baggage string. -```jsx -rootSpan = scope.getRootSpan() +```js +baggage = span.getBaggage() ``` -### Additional APIs - -span.`setStartTimestamp()` - overwrite the span’s start time - -span.`setEndTimestamp()` - overwrites the span’s end time - -span.`setStatus()` - success, error, unknown - -span.`getBaggage()` - returns the baggage string - -span.`getTraceparent()` - returns the sentry trace parent string +### Additional, optional span APIs -### Examples - -```jsx -rootSpan = sentry.tracing.startSpan({ - name: 'GET /', - op: 'http.server', -}) - -span = rootSpan.startSpan({ - name: 'middleware.handle', - op: 'http.middleware', -}) - -span.end() -rootSpan.end() -``` +`span.setStartTimestamp()` - overwrite the span's start time +`span.setEndTimestamp()` - overwrites the span's end time From 24ab5ca6a24af11204cd1bc4dd3d682e48e1c010 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 08:26:09 +0100 Subject: [PATCH 04/13] Fix formatting --- .../sdk/telemetry/spans/span-protocol.mdx | 74 +++++++++---------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx index 9c28c0480fb5e..bf6bb5cf00771 100644 --- a/develop-docs/sdk/telemetry/spans/span-protocol.mdx +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -7,7 +7,7 @@ title: Span Protocol We'll introduce a new “span” envelope item, which the SDK uses to emit a segment span and its children. And in the future, a batch of spans. The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/), which introduced typed attributes and will ease the conversion in our POtel SDKs. -```json +``` { "event_id":"9ec79c33ec9942ab8353589fcb2e04dc" } @@ -15,49 +15,49 @@ The payload of each envelope item follows the [OpenTelemetry Protocol](https://o "type": "span" } { - "traceId": "32d3c7cb501fbddbe3ce1016a72d50b5", - "spanId": "e91d37480970523b", - "name": "GET /", - "startTime": "1544712660", - "endTime": "1544712680", - "attributes": [ - { + "traceId": "32d3c7cb501fbddbe3ce1016a72d50b5", + "spanId": "e91d37480970523b", + "name": "GET /", + "startTime": "1544712660", + "endTime": "1544712680", + "attributes": [ + { "key": "sentry.op", - "value": { - "stringValue": "http.sever", - } + "value": { + "stringValue": "http.sever", + } }, - { - "key": "http.response.status_code", - "value": { - "intValue": "200", - } - } - } + { + "key": "http.response.status_code", + "value": { + "intValue": "200", + } + } + ] } { "type": "span" } { "traceId": "32d3c7cb501fbddbe3ce1016a72d50b5", - "spanId": "6b22b3af586e777a", - "parentSpanId": "e91d37480970523b", - "name": "UserMiddleware", - "startTimeUnix": "1544712665", - "endTimeUnix": "1544712675", - "attributes": [ - { - "key": "sentry.op", - "value": { - "stringValue": "middleware.handle", - } - }, - { - "key": "user.email", - "value": { - "stringValue": "jane.doe@example.com", - } - } - } + "spanId": "6b22b3af586e777a", + "parentSpanId": "e91d37480970523b", + "name": "UserMiddleware", + "startTimeUnix": "1544712665", + "endTimeUnix": "1544712675", + "attributes": [ + { + "key": "sentry.op", + "value": { + "stringValue": "middleware.handle", + } + }, + { + "key": "user.email", + "value": { + "stringValue": "jane.doe@example.com", + } + } + ] } ``` From 091eacd807c6a87a4fbdf6d6465f1c4c4c83ba1a Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 09:12:16 +0100 Subject: [PATCH 05/13] Add trace propagation --- .../sdk/telemetry/spans/span-properties.mdx | 15 ----------- .../spans/span-trace-propagation.mdx | 26 +++++++++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-) delete mode 100644 develop-docs/sdk/telemetry/spans/span-properties.mdx create mode 100644 develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx diff --git a/develop-docs/sdk/telemetry/spans/span-properties.mdx b/develop-docs/sdk/telemetry/spans/span-properties.mdx deleted file mode 100644 index d472aff3321d2..0000000000000 --- a/develop-docs/sdk/telemetry/spans/span-properties.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Span Propertires ---- - -# Span Propertires - -Instead of spans containing tags, context, and data, we'll unify all these properties into a new “attributes” property. -Similar to OTel's semantic conventions, we'll add special meaning to certain attribute keys, such as `sentry.release`, `sentry.op`, etc. - -```js -span.setAttribute('http.request.method', 'GET') -span.setAttribute('user.email', 'jane.doe@example.com') -``` - -We'll map these attributes to their respective existing property in Relay to ease the work required for the product during the transition period. diff --git a/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx b/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx new file mode 100644 index 0000000000000..29ec171b08e54 --- /dev/null +++ b/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx @@ -0,0 +1,26 @@ +--- +title: Span Trace Propagation +--- + +# Span Trace Propagation + +## Continue an incoming trace + +To continue a trace from an upstream service, the SDK must expose a method to extract the traceparent and baggage information and apply these to the applicable scope. + +```js +scope.setPropagationContext({ + traceparent: request.headers.SENTRY_TRACE, + baggage: request.headers.SENTRY_BAGGAGE, +}) +``` +Newly created root spans should now contain these properties, such as `trace_id` and `parent_span_id`. + +## Continue an outgoing trace + +To propagate a trace to a downstream service, the SDK must expose methods to fetch the required information to allow the next service to continue the trace. + +```js +traceparent = scope.getTraceparent() +baggage = scope.getBaggage() +``` From 88d3ac2540d3cf13101ea3579745e11d525cdd73 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 09:16:54 +0100 Subject: [PATCH 06/13] Clean up --- develop-docs/sdk/telemetry/spans/span-api.mdx | 16 ---------------- .../sdk/telemetry/spans/span-protocol.mdx | 4 ++-- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/span-api.mdx b/develop-docs/sdk/telemetry/spans/span-api.mdx index 2e171c745e983..e5951add03639 100644 --- a/develop-docs/sdk/telemetry/spans/span-api.mdx +++ b/develop-docs/sdk/telemetry/spans/span-api.mdx @@ -52,22 +52,6 @@ span.setAttribute(SpanAttributes.HTTP_METHOD, 'GET') span.setAttribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 200) ``` -### Receiving the trace parent - -The SDK must expose a method to receive the baggage string. - -```js -traceparent = span.getTraceparent() -``` - -### Receiving the baggage - -The SDK must expose a method to receive the baggage string. - -```js -baggage = span.getBaggage() -``` - ### Additional, optional span APIs `span.setStartTimestamp()` - overwrite the span's start time diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx index bf6bb5cf00771..0c6986eaa773a 100644 --- a/develop-docs/sdk/telemetry/spans/span-protocol.mdx +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -4,8 +4,8 @@ title: Span Protocol # Span protocol -We'll introduce a new “span” envelope item, which the SDK uses to emit a segment span and its children. And in the future, a batch of spans. -The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/), which introduced typed attributes and will ease the conversion in our POtel SDKs. +The SDK must implement a new "span" envelope item, which is used to emit an entire span tree (one root span and its children). +The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/). ``` { From 3ed61ed6d02905cb3bf454a1da6f4ef75c565786 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 09:46:51 +0100 Subject: [PATCH 07/13] Clean up headers --- develop-docs/sdk/telemetry/spans/span-api.mdx | 13 +- .../sdk/telemetry/spans/span-protocol.mdx | 2 - .../sdk/telemetry/spans/span-sampling.mdx | 144 +----------------- .../spans/span-trace-propagation.mdx | 2 - 4 files changed, 13 insertions(+), 148 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/span-api.mdx b/develop-docs/sdk/telemetry/spans/span-api.mdx index e5951add03639..cefe918082933 100644 --- a/develop-docs/sdk/telemetry/spans/span-api.mdx +++ b/develop-docs/sdk/telemetry/spans/span-api.mdx @@ -2,12 +2,10 @@ title: Span API --- -# Span API - Spans are measuring the duration of certain operations in an application. The topmost member of a span tree is called the root span. This span has no parent span and groups together its children with a representative name for the entire operation, such as `GET /` in case of a request to a backend application. -### Creating a root span +## Creating a root span The SDK must expose a method for creating a root span. The user must be able to set certain properties on this root span, such as its name, the type of operation (`op`) and others. @@ -19,7 +17,7 @@ span = sentry.tracing.startSpan() span.end() ``` -### Creating nested spans +## Creating nested spans To create nested spans, the SDK must expose an explicit way for a user to perform this task. @@ -33,7 +31,7 @@ childSpan = span.startChild() childSpan.end() ``` -### Setting the span status +## Setting the span status A span has two statuses, `ok` and `error`. By default, the status of a span is set to `ok`. The SDK must allow a user to modify the status of a span. @@ -42,7 +40,7 @@ The SDK must allow a user to modify the status of a span. span.setStatus('error') ``` -### Setting span attributes +## Setting span attributes The SDK must expose a method to allow a user to set data attributes onto a span. These attributes should use pre-defined keys whenever possible. @@ -52,7 +50,8 @@ span.setAttribute(SpanAttributes.HTTP_METHOD, 'GET') span.setAttribute(SpanAttributes.HTTP_RESPONSE_STATUS_CODE, 200) ``` -### Additional, optional span APIs +## Additional, optional span APIs `span.setStartTimestamp()` - overwrite the span's start time + `span.setEndTimestamp()` - overwrites the span's end time diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx index 0c6986eaa773a..c0ae4f2b413a6 100644 --- a/develop-docs/sdk/telemetry/spans/span-protocol.mdx +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -2,8 +2,6 @@ title: Span Protocol --- -# Span protocol - The SDK must implement a new "span" envelope item, which is used to emit an entire span tree (one root span and its children). The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/). diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index 1725ea9f2d917..cf1d72013e433 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -1,150 +1,20 @@ --- -title: Span Sampling +title: Span Sampling & Filtering --- -# Span Sampling +Any APIs exposed to the user to sample or filter spans must adhere to the following design principles: -With the metrics product shifting to a sampling based solution, extrapolation is of the utter most importance to being able to display reliable metrics to our users. We want to account for client sampling in addition to server sampling. -This requires the SDKs to always report the correct sampling rates in each tracing related envelope send to Sentry. -Directional, the goal is to create complete traces by default and wherever possible. We will not optimise for spent-control. +- The APIs are optimized for trace completness +- The APIs are optimized for conclusive sampling decisions -We historically exposed many ways to our users to remove certain transactions or spans from being emitted to Sentry. This resulted in convoluted SDK APIs, weird edge cases in the product and an overall bad user experience. More importantly, these sampling controls will contribute to vastly wrong metrics being extracted from span attributes, hence we need to rework those: +## Sample root spans with `tracesSampleRate` & `tracesSampler` -- `beforeSendTransaction` and `beforeSendSpan` will be replaced with `beforeSendSpans`, which encourages users to *mutate* spans, but they cannot be dropped through this callback. -- All SDK integrations that create spans, need to be able to be turned off via a config flag for the purpose of noise reduction or via a new `ignoreSpans` options that accepts a glob pattern. -- Sampling happens exclusively via `tracesSampleRate` or `tracesSampler`. We need to make sure to always prefer the parent sampling decision, either via explicit docs or a new argument for the `tracesSampler` or SDK option. -- Trace propagation is aware of applications or at least organizations and prevents “leaking” traces across this boundary. -## `beforeSendSpans` -The primary use-case for this hook will be data scrubbing or mutating certain properties of spans. +## Filter spans with `ignoreSpans` & integration config -We are likely only allow to mutate the span’s name, timestamps, status and most attributes. Trace ID, span ID, parent span ID are immutable, as well as certain span attributes, such as segment ID. -It is yet to be defined which arguments will be passed into the callback or how the hook behaves with transaction envelopes. -## Span configuration +## Sanitize span attributes with `beforeSendSpans` -To reduce noise, users might want to disable certain integrations creating spans. This should ideally be exposed as a global config or at an integration level. Additionally, a new `ignoreSpans` option will allow users to not emit certain spans based on their name & attributes. -```jsx -Sentry.init({ - dsn: 'foo@bar', - ignoreSpans: [ - 'GET /about', - 'events.signal *', - ], - ignoreSpans: (name, attributes) { - if ( - name === 'server.request' && - attributes['server.address'] === 'https://sentry.io' - ) { - return true - } - }, - integrations: [ - fsIntegration: { - ignoreSpans: [ - 'fs.read', - ], - readSpans: true, - writeSpans: false, - } - ] -}) -``` - -## Parent Sampling Decision - -In today's SDKs, a parent sampling decision received via a `sentry-trace` header or similar can be overruled by setting a `tracesSampler`. -As we need to optimize for trace completeness, we need to explicitly call out the impact of the sampler or change the behaviour to always use the parent’s decision unless explicitly opted-out. - -```jsx -// Explict docs -Sentry.init({ - tracesSampler: ({ name, attributes, parentSampled }) => { - // Continue trace decision, if there is any parentSampled information - // This is crucial for complete traces - if (typeof parentSampled === "boolean") { - return parentSampled; - } - - // Else, use default sample rate (replacing tracesSampleRate) - return 0.5; - }, -}) - -// Not chosen - New top level option -Sentry.init({ - ignoreParentSamplingDecision: true, - tracesSampler: ({ name, attributes, parentSampled }) => { - // Do not sample health checks ever - if (name.includes("healthcheck")) { - // Drop this transaction, by setting its sample rate to 0% - return 0.0; - } - - // Else, use default sample rate (replacing tracesSampleRate) - return 0.2; - }, -}) -``` - -## Parent Sampling Origins - -In order to filter out unrelated 3rd party services that are making requests to a Sentry instrumented app containing a `sentry-trace` header, we’ll implement RFC https://github.com/getsentry/rfcs/pull/137. This feature might be enabled by default if the: - -- SDK knows its org -- The incoming baggage header contains a `sentry-org` entry - -## Sampling Seed in DSC - -To increase the chance of capturing complete traces when users return a new sample rate `tracesSampler` in backend services, we propagate the 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. - -### Behavior - -A user can also override the parent sample rate in traces sampler. For example, a backend service has a `tracesSampler` that overrides frontend traces. This leads to three scenarios: - -- The new (backend) sample rate is lower than the parent’s (frontend): All traces captured in the backend are complete. There are additional partial traces for the frontend. -- The new (backend) sample rate is higher than the parent’s (fronted): All traces propagated from the frontend are complete. There are additional partial traces for the backend. -- Both sample rates are equal: All traces are complete, the sampling decision is fully inherited. - -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. - -### SDK Spec - -- sentry baggage gains a new field `sentry-sample_rand` - - when a new trace is started, `sentry-sample_rand` is filled with a truly random number. this also applies when the trace’s sample rate is 1.0 - - for inbound traces without a `sentry-sample_rand` (from old SDKs), the SDK inserts a new truly random number on-the-fly. -- sampling decisions in the SDK that currently compare `sentry-sample_rand` from the trace instead of `math.random()` with the sample rate. - - when traces sampler is invoked, this also applies to the return value of traces sampler. ie. `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` - - There is no more `math.random()` directly involved in any sampling decision. -- in traces sampler, 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. - - ```jsx - 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; - }, - - ``` - - - if the `sentry-sample_rate` (`parentSampleRate`) is not available for any reason for an inbound trace, but the trace has the sampled flag set to `true`, the SDK injects `parentSampleRate: 1.0` into the callback. - -## Baggage Freeze - ---- - -We accept partial traces under the assumption that the transaction name is mostly changed early in the request cycle. - -# External Resources - -https://opentelemetry.io/docs/specs/otel/trace/tracestate-probability-sampling-experimental/ diff --git a/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx b/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx index 29ec171b08e54..e95a4f6dc9d2c 100644 --- a/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx +++ b/develop-docs/sdk/telemetry/spans/span-trace-propagation.mdx @@ -2,8 +2,6 @@ title: Span Trace Propagation --- -# Span Trace Propagation - ## Continue an incoming trace To continue a trace from an upstream service, the SDK must expose a method to extract the traceparent and baggage information and apply these to the applicable scope. From 072bc243dc4af3e4806e0154adbf96946a846736 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 12:16:11 +0100 Subject: [PATCH 08/13] Add sampling --- .../sdk/telemetry/spans/span-sampling.mdx | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index cf1d72013e433..e2d61f810cb5e 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -4,17 +4,99 @@ title: Span Sampling & Filtering Any APIs exposed to the user to sample or filter spans must adhere to the following design principles: -- The APIs are optimized for trace completness +- The APIs are optimized for trace completeness - The APIs are optimized for conclusive sampling decisions ## Sample root spans with `tracesSampleRate` & `tracesSampler` +The SDK is automatically initialized with a `tracesSampleRate` of `0`. +When starting a root span, the configured rate is compared against a random number between 0 and 1 to decide if this root span will be sampled or not. +If the SDK is configured with a `tracesSampler`, the `tracesSampleRate` no longer applies. +The `tracesSampler` callback must receive sufficient arguments from users to define their own sampling rules. +This can include but is not limited to certain attributes from the root span, such as HTTP headers. +The return value of the `tracesSampler` is a float between `0.0` and `1.0`. + +```js +Sentry.init({ + tracesSampler: ({ name, attributes, parentSampleRate }) => { + // Inherit the trace parent's sample rate if there is one. Sampling is deterministic + // for one trace, e.g. if the parent was sampled, all children will be sampled at the same rate. + if (typeof parentSampleRate === "number") { + return parentSampleRate; + } + + // Else, use a default sample rate (replacing tracesSampleRate). + return 0.5; + } +}) +``` + +The `parentSampleRate` is a propagated value inside the baggage, using key `sentry-sample_rand`. +The value stems from a truly random number between 0 and 1, generated when a new trace is started. If the SDK does not receive such a number in an incoming trace, a new, truly random number between 0 and 1 is generated. + +In the following cases, the SDK must compare sample rates against this `parentSampleRate` instead of `math.random()`: + + - When a `tracesSampler` is configured, e.g. `trace["sentry-sample_rand"] < tracesSampler()` + + - When the SDK is the head of trace, this applies to sample decisions based on `tracesSampleRate`, e.g. `trace['sentry-sample_rand'] < config.tracesSampleRate` + +If the `sentry-sample_rate` (`parentSampleRate`) is not available for any reason for an inbound trace, but the trace has the sampled flag set to true, the SDK injects `parentSampleRate: 1.0` into the `tracesSampler`. + +If no `tracesSampler` is configured, a propagated sampling decision via the traceparent takes precedence over the `tracesSampleRate`. This behavior can be disabled by defining a `tracesSampler`. + +### Parent Sampling Origins + +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. + +The SDK must be configurable with an optional `org: ` setting that takes precedence over the parsed value from the DSN. ## Filter spans with `ignoreSpans` & integration config +The SDK must implement a mechanism for users to filter out spans. The result must be binary (true/false). +The `ignoreSpans` option accepts a glob pattern or string. +The `integrations` option can perform in similar fashion or make explicit opt-out possible via a bool flag. +If both options are not feasible to be implemented in certain SDKs, other approaches must be explored that have the same outcome. + +```js +Sentry.init({ + ignoreSpans: [ + 'GET /about', + 'events.signal *', + ], + integrations: [ + fsIntegration: { + ignoreSpans: [ + 'fs.read', + ], + readSpans: true, + writeSpans: false, + } + ] +}) +``` ## Sanitize span attributes with `beforeSendSpans` +This callback must not allow the removal of any spans from the span tree. +It receives a deep copy of all spans in the span tree and their attributes. + +``` +[ + { + 'name': 'GET /', + 'attributes': [ + 'http.request.method': 'GET', + 'http.response.status_code': 200, + ] + }, +] +``` +Users can mutate any exposed properties to perform sanitation on sensitive data or Pii. +The return value `beforeSendSpans` should be merged with the original span tree prior to emission. From f3166a57b272e23a3e4aa8405e37cdd438c513c9 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 12:26:06 +0100 Subject: [PATCH 09/13] Formatting --- develop-docs/sdk/telemetry/spans/span-sampling.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index e2d61f810cb5e..9f3d1dc0122ad 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -45,7 +45,7 @@ If the `sentry-sample_rate` (`parentSampleRate`) is not available for any reason If no `tracesSampler` is configured, a propagated sampling decision via the traceparent takes precedence over the `tracesSampleRate`. This behavior can be disabled by defining a `tracesSampler`. -### Parent Sampling Origins +## Parent Sampling Origins 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`. From 291783671a392de5c6b8cca14b89acff489bc733 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 12:28:14 +0100 Subject: [PATCH 10/13] Add notes --- develop-docs/sdk/telemetry/spans/scopes.mdx | 10 ++++++++++ develop-docs/sdk/telemetry/spans/span-protocol.mdx | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/develop-docs/sdk/telemetry/spans/scopes.mdx b/develop-docs/sdk/telemetry/spans/scopes.mdx index e69de29bb2d1d..dd9c3adcdcd84 100644 --- a/develop-docs/sdk/telemetry/spans/scopes.mdx +++ b/develop-docs/sdk/telemetry/spans/scopes.mdx @@ -0,0 +1,10 @@ +--- +title: Scopes +--- + + + This is work in progress and does not represent the final state. + + +This is a more concise version of the [Hub & Scope Refactoring](/sdk/miscellaneous/hub_and_scope_refactoring/) document. It does focus on the actual implementation and expected features. +The old document remains unchanged, and offers more historical context and migration strategies. diff --git a/develop-docs/sdk/telemetry/spans/span-protocol.mdx b/develop-docs/sdk/telemetry/spans/span-protocol.mdx index c0ae4f2b413a6..1de76abe4e9ae 100644 --- a/develop-docs/sdk/telemetry/spans/span-protocol.mdx +++ b/develop-docs/sdk/telemetry/spans/span-protocol.mdx @@ -2,6 +2,10 @@ title: Span Protocol --- + + This is work in progress and does not represent the final state. + + The SDK must implement a new "span" envelope item, which is used to emit an entire span tree (one root span and its children). The payload of each envelope item follows the [OpenTelemetry Protocol](https://opentelemetry.io/docs/specs/otel/protocol/). From 7a8e6257dd6b76a61c0e9b0b002cfc961c674ff5 Mon Sep 17 00:00:00 2001 From: Michi Hoffmann Date: Fri, 8 Nov 2024 13:45:24 +0100 Subject: [PATCH 11/13] Update develop-docs/sdk/telemetry/spans/span-sampling.mdx Co-authored-by: Jan Michael Auer --- .../sdk/telemetry/spans/span-sampling.mdx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index 9f3d1dc0122ad..399405ff1e7a9 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -66,18 +66,18 @@ If both options are not feasible to be implemented in certain SDKs, other approa ```js Sentry.init({ ignoreSpans: [ - 'GET /about', - 'events.signal *', - ], - integrations: [ - fsIntegration: { - ignoreSpans: [ - 'fs.read', - ], - readSpans: true, - writeSpans: false, - } - ] + 'GET /about', + 'events.signal *', + ], + integrations: [ + fsIntegration: { + ignoreSpans: [ + 'fs.read', + ], + readSpans: true, + writeSpans: false, + } + ] }) ``` From 426c807a10d0ef7894e2e765030b4148ec8e9b87 Mon Sep 17 00:00:00 2001 From: Michi Hoffmann Date: Fri, 8 Nov 2024 13:47:22 +0100 Subject: [PATCH 12/13] Update develop-docs/sdk/telemetry/spans/span-sampling.mdx Co-authored-by: Jan Michael Auer --- develop-docs/sdk/telemetry/spans/span-sampling.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/develop-docs/sdk/telemetry/spans/span-sampling.mdx b/develop-docs/sdk/telemetry/spans/span-sampling.mdx index 399405ff1e7a9..04cd8d7ac047b 100644 --- a/develop-docs/sdk/telemetry/spans/span-sampling.mdx +++ b/develop-docs/sdk/telemetry/spans/span-sampling.mdx @@ -37,7 +37,7 @@ The value stems from a truly random number between 0 and 1, generated when a new In the following cases, the SDK must compare sample rates against this `parentSampleRate` instead of `math.random()`: - - When a `tracesSampler` is configured, e.g. `trace["sentry-sample_rand"] < tracesSampler()` + - When a `tracesSampler` is configured, i.e. `trace["sentry-sample_rand"] < tracesSampler()` - When the SDK is the head of trace, this applies to sample decisions based on `tracesSampleRate`, e.g. `trace['sentry-sample_rand'] < config.tracesSampleRate` From b66bae741719f0bb81828a7653631b8ac1064dd1 Mon Sep 17 00:00:00 2001 From: Michael Hoffmann Date: Fri, 8 Nov 2024 16:21:49 +0100 Subject: [PATCH 13/13] WIP scopes --- develop-docs/sdk/telemetry/spans/scopes.mdx | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/develop-docs/sdk/telemetry/spans/scopes.mdx b/develop-docs/sdk/telemetry/spans/scopes.mdx index dd9c3adcdcd84..c5afbddc81958 100644 --- a/develop-docs/sdk/telemetry/spans/scopes.mdx +++ b/develop-docs/sdk/telemetry/spans/scopes.mdx @@ -6,5 +6,55 @@ title: Scopes This is work in progress and does not represent the final state. +The implementation in each SDK consists of three types of scopes: + +- The global scope +- The isolation scope +- The current scope + +Regardles of the scope type, data like tags, breadcrumbs, context, etc. can be added to each scope type by the user. + +### Global Scope + +The global scope acts as a global variable that stays the same for the entire execution of the application. Data applied to this scope will be applied to **all** events emitted by the SDK. +This scope is used for application-wide data like the `release`, the `environment`, etc. + +### Isolation Scope + +The isolation scope holds data which is only applicable to the current request (on a server), tab (in a browser), or user (on mobile). Top-level SDK APIs like `sentry.setTag()`, `sentry.setContext()`, etc. write to the isolation scope. + +The isolation scope is stored in a context variable, thread local, async local, or similar depending on the platform. + +The isolation scope is forked by the SDK's integrations. Users should not need to think about isolation scopes or forking of one. + +### Current Scope + +This scope holds data for the current active span. Whenever a new span is started the current scope of the parent span is forked (read: duplicated), giving the new span all the data from the parent span and making it possible to add or mutate data that is just applied to the new span (see also "Copy-on-write"). + +Changing the original scope after forking does not modify the forked scope. + +The current scope is stored in a context variable, thread local, async local, or similar depending on the platform. + +The current scope can be forked by the end user. Either explicitly, by using `sentry.withScope()` or implicitly, by starting a new span. + +### The Scope APIs + +```js +... +``` + +### Applying the scope data to events + +The data held by all three scope types is merged before it is applied to an event. + +This is performed in the following order: + +1. Data from the global scope is... +2. merged with data from the isolation scope, which is... +3. merged with data from the current scope, which is ... +4. applied to the event + +## Related Documents + This is a more concise version of the [Hub & Scope Refactoring](/sdk/miscellaneous/hub_and_scope_refactoring/) document. It does focus on the actual implementation and expected features. The old document remains unchanged, and offers more historical context and migration strategies.