Skip to content

ref: Make dynamic sampling context mutable #5710

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 42 commits into from
Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
7bba4ca
ref: Make dynamic sampling context mutable
lforst Sep 8, 2022
f6a59b1
please linter
lforst Sep 8, 2022
64c9935
Merge remote-tracking branch 'origin/master' into lforst-mutable-dsc
lforst Sep 9, 2022
7a8608c
fix appendage
lforst Sep 9, 2022
4f7d68a
fix(tests): Fix node integration tests sharing SDK initializations
lforst Sep 9, 2022
ee8447a
Merge remote-tracking branch 'origin/master' into lforst-fix-node-int…
lforst Sep 9, 2022
365e8a9
Try readding forceExit
lforst Sep 9, 2022
9831bec
Merge branch 'lforst-fix-node-integration-tests' into lforst-mutable-dsc
lforst Sep 9, 2022
d1a348d
Merge remote-tracking branch 'origin/master' into lforst-mutable-dsc
lforst Sep 12, 2022
248a9b6
Freeze DSC with empty values when there is a sentry-trace header but …
lforst Sep 12, 2022
3074bc5
Close server on test - even on request error
lforst Sep 12, 2022
5da55e6
Fix some node integration tests
lforst Sep 12, 2022
7a85889
Adjust tests checking for empty baggage header
lforst Sep 12, 2022
b0b65c7
test: Run node integration tests in isolation
lforst Sep 12, 2022
a1811ce
Merge branch 'lforst-isolated-node-integration-tests' into lforst-mut…
lforst Sep 12, 2022
e4b7631
Avoid port race condition
lforst Sep 12, 2022
906a116
Merge branch 'lforst-isolated-node-integration-tests' into lforst-mut…
lforst Sep 12, 2022
97a13b9
Better log message
lforst Sep 12, 2022
b86bdb7
Better log message
lforst Sep 12, 2022
335a646
Fix first round of tests
lforst Sep 12, 2022
dd5fe3f
Avoid port race condition (finally pls)
lforst Sep 12, 2022
8028d9e
Small readability improvements
lforst Sep 12, 2022
27a7afc
Merge branch 'lforst-isolated-node-integration-tests' into lforst-mut…
lforst Sep 12, 2022
126e3b1
Fix some more tests
lforst Sep 12, 2022
0e3c3d3
Fix serverless tests
lforst Sep 12, 2022
fbd7226
Merge remote-tracking branch 'origin/master' into lforst-mutable-dsc
lforst Sep 12, 2022
969567d
Why did I try to refactor things
lforst Sep 13, 2022
28b7789
Fix bug
lforst Sep 13, 2022
32a9fc0
Merge remote-tracking branch 'origin/master' into lforst-mutable-dsc
lforst Sep 13, 2022
2d339ba
Fix nextjs unit tests
lforst Sep 13, 2022
a76c891
Add handling for empty baggage header
lforst Sep 13, 2022
9cf8644
fix(utils): Make empty traceparent string count as invalid
lforst Sep 13, 2022
9d4b623
Merge branch 'lforst-handle-empty-traceparentdata' into lforst-mutabl…
lforst Sep 13, 2022
3d953ad
Minor comments
lforst Sep 13, 2022
08c708a
Smol refactor
lforst Sep 13, 2022
ed68239
Add tests
lforst Sep 13, 2022
fafc4af
Make DSC read from transaction readonly
lforst Sep 14, 2022
5580981
Move down dynamicSamplingProps
lforst Sep 14, 2022
7fba2fb
Spread instead of push
lforst Sep 14, 2022
18df790
Save a byte
lforst Sep 14, 2022
55aa327
Always return object in getDynamicSamplingContext
lforst Sep 14, 2022
bf06b7b
Fix test
lforst Sep 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions packages/core/src/envelope.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {
Baggage,
DsnComponents,
DynamicSamplingContext,
Event,
EventEnvelope,
EventEnvelopeHeaders,
Expand All @@ -13,7 +11,7 @@ import {
SessionEnvelope,
SessionItem,
} from '@sentry/types';
import { createEnvelope, dropUndefinedKeys, dsnToString, getSentryBaggageItems } from '@sentry/utils';
import { createEnvelope, dropUndefinedKeys, dsnToString } from '@sentry/utils';

/** Extract sdk info from from the API metadata */
function getSdkMetadataForEnvelopeHeader(metadata?: SdkMetadata): SdkInfo | undefined {
Expand Down Expand Up @@ -101,8 +99,7 @@ function createEventEnvelopeHeaders(
tunnel: string | undefined,
dsn: DsnComponents,
): EventEnvelopeHeaders {
const baggage: Baggage | undefined = event.sdkProcessingMetadata && event.sdkProcessingMetadata.baggage;
const dynamicSamplingContext = baggage && getSentryBaggageItems(baggage);
const dynamicSamplingContext = event.sdkProcessingMetadata && event.sdkProcessingMetadata.dynamicSamplingContext;

return {
event_id: event.event_id as string,
Expand All @@ -111,7 +108,7 @@ function createEventEnvelopeHeaders(
...(!!tunnel && { dsn: dsnToString(dsn) }),
...(event.type === 'transaction' &&
dynamicSamplingContext && {
trace: dropUndefinedKeys({ ...dynamicSamplingContext }) as DynamicSamplingContext,
trace: dropUndefinedKeys({ ...dynamicSamplingContext }),
}),
};
}
31 changes: 16 additions & 15 deletions packages/core/test/lib/envelope.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('createEventEnvelope', () => {
{
type: 'transaction',
sdkProcessingMetadata: {
baggage: [{ trace_id: '1234', public_key: 'pubKey123' }, '', false],
dynamicSamplingContext: { trace_id: '1234', public_key: 'pubKey123' },
},
},
{ trace_id: '1234', public_key: 'pubKey123' },
Expand All @@ -30,7 +30,12 @@ describe('createEventEnvelope', () => {
{
type: 'transaction',
sdkProcessingMetadata: {
baggage: [{ environment: 'prod', release: '1.0.0', public_key: 'pubKey123', trace_id: '1234' }, '', false],
dynamicSamplingContext: {
environment: 'prod',
release: '1.0.0',
public_key: 'pubKey123',
trace_id: '1234',
},
},
},
{ release: '1.0.0', environment: 'prod', trace_id: '1234', public_key: 'pubKey123' },
Expand All @@ -40,19 +45,15 @@ describe('createEventEnvelope', () => {
{
type: 'transaction',
sdkProcessingMetadata: {
baggage: [
{
environment: 'prod',
release: '1.0.0',
transaction: 'TX',
user_segment: 'segmentA',
sample_rate: '0.95',
public_key: 'pubKey123',
trace_id: '1234',
},
'',
false,
],
dynamicSamplingContext: {
environment: 'prod',
release: '1.0.0',
transaction: 'TX',
user_segment: 'segmentA',
sample_rate: '0.95',
public_key: 'pubKey123',
trace_id: '1234',
},
},
},
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hasTracingEnabled } from '@sentry/tracing';
import { serializeBaggage } from '@sentry/utils';
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
import { GetServerSideProps } from 'next';

import { isBuild } from '../../utils/isBuild';
Expand Down Expand Up @@ -45,7 +45,9 @@ export function withSentryGetServerSideProps(
const requestTransaction = getTransactionFromRequest(req);
if (requestTransaction) {
serverSideProps.props._sentryTraceData = requestTransaction.toTraceparent();
serverSideProps.props._sentryBaggage = serializeBaggage(requestTransaction.getBaggage());

const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext();
serverSideProps.props._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hasTracingEnabled } from '@sentry/tracing';
import { serializeBaggage } from '@sentry/utils';
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
import App from 'next/app';

import { isBuild } from '../../utils/isBuild';
Expand Down Expand Up @@ -51,7 +51,10 @@ export function withSentryServerSideAppGetInitialProps(origAppGetInitialProps: A
const requestTransaction = getTransactionFromRequest(req!);
if (requestTransaction) {
appGetInitialProps.pageProps._sentryTraceData = requestTransaction.toTraceparent();
appGetInitialProps.pageProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage());

const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext();
appGetInitialProps.pageProps._sentryBaggage =
dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
}

return appGetInitialProps;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hasTracingEnabled } from '@sentry/tracing';
import { serializeBaggage } from '@sentry/utils';
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
import { NextPageContext } from 'next';
import { ErrorProps } from 'next/error';

Expand Down Expand Up @@ -52,7 +52,9 @@ export function withSentryServerSideErrorGetInitialProps(
const requestTransaction = getTransactionFromRequest(req!);
if (requestTransaction) {
errorGetInitialProps._sentryTraceData = requestTransaction.toTraceparent();
errorGetInitialProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage());

const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext();
errorGetInitialProps._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
}

return errorGetInitialProps;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { hasTracingEnabled } from '@sentry/tracing';
import { serializeBaggage } from '@sentry/utils';
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
import { NextPage } from 'next';

import { isBuild } from '../../utils/isBuild';
Expand Down Expand Up @@ -42,7 +42,9 @@ export function withSentryServerSideGetInitialProps(origGetInitialProps: GetInit
const requestTransaction = getTransactionFromRequest(req!);
if (requestTransaction) {
initialProps._sentryTraceData = requestTransaction.toTraceparent();
initialProps._sentryBaggage = serializeBaggage(requestTransaction.getBaggage());

const dynamicSamplingContext = requestTransaction.getDynamicSamplingContext();
initialProps._sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is repeated 4x - can we extract into a helper func? Can do this in another PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We intentionally kept it this way for the ongoing Next.js changes and said we were gonna extract duplicate logic later, since we don't know yet if extracting it now will complicate things

}

return initialProps;
Expand Down
8 changes: 4 additions & 4 deletions packages/nextjs/src/config/wrappers/wrapperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { captureException, getCurrentHub, startTransaction } from '@sentry/core'
import { addRequestDataToEvent } from '@sentry/node';
import { getActiveTransaction } from '@sentry/tracing';
import { Transaction } from '@sentry/types';
import { extractTraceparentData, fill, isString, parseBaggageSetMutability } from '@sentry/utils';
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData, fill } from '@sentry/utils';
import * as domain from 'domain';
import { IncomingMessage, ServerResponse } from 'http';

Expand Down Expand Up @@ -88,19 +88,19 @@ export function callTracedServerSideDataFetcher<F extends (...args: any[]) => Pr

if (requestTransaction === undefined) {
const sentryTraceHeader = req.headers['sentry-trace'];
const rawBaggageString = req.headers && isString(req.headers.baggage) && req.headers.baggage;
const rawBaggageString = req.headers && req.headers.baggage;
const traceparentData =
typeof sentryTraceHeader === 'string' ? extractTraceparentData(sentryTraceHeader) : undefined;

const baggage = parseBaggageSetMutability(rawBaggageString, traceparentData);
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(rawBaggageString);

const newTransaction = startTransaction({
op: 'nextjs.data.server',
name: options.requestedRouteName,
...traceparentData,
metadata: {
source: 'route',
baggage,
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we have an invalid traceparentData, do we want to even include a dynamicSamplingContext? Setting the dynamicSamplingContext to be an empty object will freeze this, which doesn't make a ton of sense to me.

We could either:
a) not freeze baggage if it comes in as an empty object (invalid - didn't have a trace_id or public_key)
b) make sure that we only define dynamicSamplingContext if traceparentData is defined. This means we still have the possibility of an empty object, but should be fine since that means the parent sdk just messed something up.

I'm leaning toward b - so I would do something like so:

...(traceparentData && dynamicSamplingContext && { dynamicSamplingContext }),

We could also construct a helper for this, since we repeat this logic in some other places.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this ternary here was to freeze baggage if there is a sentry-trace header, but no baggage header. The freezing is intentional and to handle requests of old SDKs correctly. I confirmed with Jan that we want to keep this behavior. Sadly, this means we can't do the thing you suggested.

In normal circumstances, it shouldn't happen that there is a baggage header with sentry- values but no sentry-trace header. This is why I didn't put any handling for that case here.

Do you have any other thoughts regarding this, otherwise I would leave it as is. Having a helper seems overkill to me for a simple if statement (even though I admit it is repeated in a lot of places).

What I can do, is add a comment where we do this to explain that DSC needs to be frozen when there is trace parent data. Wdyt?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this ternary here was to freeze baggage if there is a sentry-trace header, but no baggage header

Ahhhh I see - this makes sense. Let me try playing around with the helper to clean it up a little.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I give up - I think we keep this for now then.

},
});

Expand Down
5 changes: 3 additions & 2 deletions packages/nextjs/src/performance/client.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getCurrentHub } from '@sentry/hub';
import { Primitive, TraceparentData, Transaction, TransactionContext, TransactionSource } from '@sentry/types';
import {
baggageHeaderToDynamicSamplingContext,
extractTraceparentData,
getGlobalObject,
logger,
parseBaggageHeader,
stripUrlQueryAndFragment,
} from '@sentry/utils';
import type { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils';
Expand Down Expand Up @@ -128,6 +128,7 @@ export function nextRouterInstrumentation(

if (startTransactionOnPageLoad) {
const source = route ? 'route' : 'url';
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage);

activeTransaction = startTransactionCb({
name: prevLocationName,
Expand All @@ -137,7 +138,7 @@ export function nextRouterInstrumentation(
...traceParentData,
metadata: {
source,
...(baggage && { baggage: parseBaggageHeader(baggage) }),
dynamicSamplingContext: traceParentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
},
});
}
Expand Down
8 changes: 4 additions & 4 deletions packages/nextjs/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import {
import { extractTraceparentData, getActiveTransaction, hasTracingEnabled } from '@sentry/tracing';
import {
addExceptionMechanism,
baggageHeaderToDynamicSamplingContext,
fill,
isString,
logger,
parseBaggageSetMutability,
stripUrlQueryAndFragment,
} from '@sentry/utils';
import * as domain from 'domain';
Expand Down Expand Up @@ -255,8 +255,8 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
__DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
}

const rawBaggageString = nextReq.headers && isString(nextReq.headers.baggage) && nextReq.headers.baggage;
const baggage = parseBaggageSetMutability(rawBaggageString, traceparentData);
const baggageHeader = nextReq.headers && nextReq.headers.baggage;
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader);

// pull off query string, if any
const reqPath = stripUrlQueryAndFragment(nextReq.url);
Expand All @@ -270,7 +270,7 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
name: `${namePrefix}${reqPath}`,
op: 'http.server',
metadata: {
baggage,
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
requestPath: reqPath,
// TODO: Investigate if there's a way to tell if this is a dynamic route, so that we can make this more
// like `source: isDynamicRoute? 'url' : 'route'`
Expand Down
11 changes: 7 additions & 4 deletions packages/nextjs/src/utils/withSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { extractTraceparentData, hasTracingEnabled } from '@sentry/tracing';
import { Transaction } from '@sentry/types';
import {
addExceptionMechanism,
baggageHeaderToDynamicSamplingContext,
isString,
logger,
objectify,
parseBaggageSetMutability,
stripUrlQueryAndFragment,
} from '@sentry/utils';
import * as domain from 'domain';
Expand Down Expand Up @@ -51,8 +51,8 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler =
__DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
}

const rawBaggageString = req.headers && isString(req.headers.baggage) && req.headers.baggage;
const baggage = parseBaggageSetMutability(rawBaggageString, traceparentData);
const baggageHeader = req.headers && req.headers.baggage;
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader);

const url = `${req.url}`;
// pull off query string, if any
Expand All @@ -72,7 +72,10 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler =
name: `${reqMethod}${reqPath}`,
op: 'http.server',
...traceparentData,
metadata: { baggage, source: 'route' },
metadata: {
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
source: 'route',
},
},
// extra context passed to the `tracesSampler`
{ request: req },
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/test/performance/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('nextRouterInstrumentation', () => {
},
metadata: {
source: 'route',
baggage: [{ environment: 'myEnv', release: '2.1.0' }, '', true],
dynamicSamplingContext: { environment: 'myEnv', release: '2.1.0' },
},
traceId: 'c82b8554881b4d28ad977de04a4fb40a',
parentSpanId: 'a755953cd3394d5f',
Expand All @@ -168,7 +168,7 @@ describe('nextRouterInstrumentation', () => {
},
metadata: {
source: 'route',
baggage: [{ environment: 'myEnv', release: '2.1.0' }, '', true],
dynamicSamplingContext: { environment: 'myEnv', release: '2.1.0' },
},
traceId: 'c82b8554881b4d28ad977de04a4fb40a',
parentSpanId: 'a755953cd3394d5f',
Expand Down
1 change: 0 additions & 1 deletion packages/nextjs/test/utils/withSentry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ describe('withSentry', () => {
op: 'http.server',

metadata: {
baggage: expect.any(Array),
source: 'route',
},
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ test('Should propagate sentry trace baggage data from an incoming to an outgoing
});
});

test('Should propagate empty baggage if sentry-trace header is present in incoming request but no baggage header', async () => {
test('Should not propagate baggage if sentry-trace header is present in incoming request but no baggage header', async () => {
const env = await TestEnv.init(__dirname, `${path.resolve(__dirname, '..')}/server.ts`);

const response = (await env.getAPIResponse(`${env.url}/express`, {
Expand All @@ -46,12 +46,11 @@ test('Should propagate empty baggage if sentry-trace header is present in incomi
expect(response).toMatchObject({
test_data: {
host: 'somewhere.not.sentry',
baggage: '',
},
});
});

test('Should propagate empty sentry and ignore original 3rd party baggage entries if sentry-trace header is present', async () => {
test('Should not propagate baggage and ignore original 3rd party baggage entries if sentry-trace header is present', async () => {
const env = await TestEnv.init(__dirname, `${path.resolve(__dirname, '..')}/server.ts`);

const response = (await env.getAPIResponse(`${env.url}/express`, {
Expand All @@ -63,7 +62,6 @@ test('Should propagate empty sentry and ignore original 3rd party baggage entrie
expect(response).toMatchObject({
test_data: {
host: 'somewhere.not.sentry',
baggage: '',
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ test('should ignore sentry-values in `baggage` header of a third party vendor an
expect(response).toMatchObject({
test_data: {
host: 'somewhere.not.sentry',
baggage: 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv',
baggage: [
'other=vendor,foo=bar,third=party,sentry-release=9.9.9,sentry-environment=staging,sentry-sample_rate=0.54,last=item',
'sentry-release=2.1.0,sentry-environment=myEnv',
],
},
});
});
Expand All @@ -29,9 +32,12 @@ test('should ignore sentry-values in `baggage` header of a third party vendor an
expect(response).toMatchObject({
test_data: {
host: 'somewhere.not.sentry',
baggage: expect.stringContaining(
'other=vendor,foo=bar,third=party,last=item,sentry-environment=prod,sentry-release=1.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public',
),
baggage: [
'other=vendor,foo=bar,third=party,sentry-release=9.9.9,sentry-environment=staging,sentry-sample_rate=0.54,last=item',
expect.stringMatching(
/sentry-environment=prod,sentry-release=1\.0,sentry-transaction=GET%20%2Ftest%2Fexpress,sentry-public_key=public,sentry-trace_id=[0-9a-f]{32},sentry-sample_rate=1/,
),
],
},
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ test('should merge `baggage` header of a third party vendor with the Sentry DSC
expect(response).toMatchObject({
test_data: {
host: 'somewhere.not.sentry',
baggage: 'other=vendor,foo=bar,third=party,sentry-release=2.0.0,sentry-environment=myEnv',
baggage: ['other=vendor,foo=bar,third=party', 'sentry-release=2.0.0,sentry-environment=myEnv'],
},
});
});
Loading