From d955dba763d01d6ef4fb94c9432a47f27ce22322 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:00:22 -0800 Subject: [PATCH 1/9] add TransactionMetadata type to @sentry/types --- packages/tracing/src/transaction.ts | 9 +++++++-- packages/types/src/debugMeta.ts | 9 ++++----- packages/types/src/index.ts | 1 + packages/types/src/transaction.ts | 9 +++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 829399f21594..69375ea8a6d3 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -1,11 +1,16 @@ import { getCurrentHub, Hub } from '@sentry/hub'; -import { DebugMeta, Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; +import { + Event, + Measurements, + Transaction as TransactionInterface, + TransactionContext, + TransactionMetadata, +} from '@sentry/types'; import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; import { computeTracestateValue } from './utils'; -type TransactionMetadata = Pick; /** JSDoc */ export class Transaction extends SpanClass implements TransactionInterface { diff --git a/packages/types/src/debugMeta.ts b/packages/types/src/debugMeta.ts index 007ab2d2d173..f28e7afad107 100644 --- a/packages/types/src/debugMeta.ts +++ b/packages/types/src/debugMeta.ts @@ -1,13 +1,12 @@ +import { TransactionMetadata } from './transaction'; + /** * Holds meta information to customize the behavior of sentry's event processing. **/ -export interface DebugMeta { +export type DebugMeta = { images?: Array; - transactionSampling?: { rate?: number; method?: string }; - /** The Sentry portion of a transaction's tracestate header, used for dynamic sampling */ - tracestate?: string; -} +} & TransactionMetadata; /** * Possible choices for debug images. diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index c2eb73188490..411ee5a1749c 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -34,6 +34,7 @@ export { TraceparentData, Transaction, TransactionContext, + TransactionMetadata, TransactionSamplingMethod, } from './transaction'; export { Thread } from './thread'; diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index d80b731b74fc..f6669322cd7b 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -119,3 +119,12 @@ export enum TransactionSamplingMethod { Rate = 'client_rate', Inheritance = 'inheritance', } + +export interface TransactionMetadata { + transactionSampling?: { rate?: number; method?: string }; + + /** The sentry half of a transaction's tracestate header, used for dynamic sampling */ + tracestate?: { + sentry?: string; + }; +} From f70e3342d7467a51ecfc3a717fa888b843cab8e9 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:26:23 -0800 Subject: [PATCH 2/9] move method for getting new header from Transaction class to Span class --- packages/tracing/src/browser/request.ts | 2 +- packages/tracing/src/span.ts | 32 ++++++++++++++++++++++++- packages/tracing/src/transaction.ts | 21 +--------------- packages/tracing/test/span.test.ts | 2 +- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/packages/tracing/src/browser/request.ts b/packages/tracing/src/browser/request.ts index 081848ee51b4..39c8a94e2c71 100644 --- a/packages/tracing/src/browser/request.ts +++ b/packages/tracing/src/browser/request.ts @@ -1,7 +1,7 @@ import { getCurrentHub } from '@sentry/hub'; +import { Span } from '@sentry/types'; import { addInstrumentationHandler, isInstanceOf, isMatchingPattern } from '@sentry/utils'; -import { Span } from '../span'; import { getActiveTransaction, hasTracingEnabled } from '../utils'; export const DEFAULT_TRACING_ORIGINS = ['localhost', /^\//]; diff --git a/packages/tracing/src/span.ts b/packages/tracing/src/span.ts index 0f6fd8750c00..f5ebcfedd46c 100644 --- a/packages/tracing/src/span.ts +++ b/packages/tracing/src/span.ts @@ -1,8 +1,10 @@ /* eslint-disable max-lines */ -import { Primitive, Span as SpanInterface, SpanContext, TraceHeaders, Transaction } from '@sentry/types'; +import { getCurrentHub } from '@sentry/hub'; +import { Hub, Primitive, Span as SpanInterface, SpanContext, TraceHeaders, Transaction } from '@sentry/types'; import { dropUndefinedKeys, timestampWithMs, uuid4 } from '@sentry/utils'; import { SpanStatus } from './spanstatus'; +import { computeTracestateValue } from './utils'; /** * Keeps track of finished spans for a given transaction @@ -352,4 +354,32 @@ export class Span implements SpanInterface { trace_id: this.traceId, }); } + + /** + * Create a new Sentry tracestate header entry (i.e. `sentry=xxxxxx`) + * + * @returns The new Sentry tracestate entry, or undefined if there's no client or no dsn + */ + protected _getNewTracestate(hub: Hub = getCurrentHub()): string | undefined { + const client = hub.getClient(); + const dsn = client?.getDsn(); + + if (!client || !dsn) { + return; + } + + const { environment, release } = client.getOptions() || {}; + + // TODO - the only reason we need the non-null assertion on `dsn.publicKey` (below) is because `dsn.publicKey` has + // to be optional while we transition from `dsn.user` -> `dsn.publicKey`. Once `dsn.user` is removed, we can make + // `dsn.publicKey` required and remove the `!`. + + return `sentry=${computeTracestateValue({ + trace_id: this.traceId, + environment, + release, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + public_key: dsn.publicKey!, + })}`; + } } diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index 69375ea8a6d3..a0dbb53a3072 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -9,7 +9,6 @@ import { import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; -import { computeTracestateValue } from './utils'; /** JSDoc */ @@ -49,7 +48,7 @@ export class Transaction extends SpanClass implements TransactionInterface { // _getNewTracestate only returns undefined in the absence of a client or dsn, in which case it doesn't matter what // the header values are - nothing can be sent anyway - so the third alternative here is just to make TS happy - this.tracestate = transactionContext.tracestate || this._getNewTracestate() || 'things are broken'; + this.tracestate = transactionContext.tracestate || this._getNewTracestate(this._hub) || 'things are broken'; // this is because transactions are also spans, and spans have a transaction pointer this.transaction = this; @@ -174,26 +173,8 @@ export class Transaction extends SpanClass implements TransactionInterface { return this; } - /** - * Create a new tracestate header value - * - * @returns The new tracestate value, or undefined if there's no client or no dsn - */ - private _getNewTracestate(): string | undefined { - const client = this._hub.getClient(); - const dsn = client?.getDsn(); - if (!client || !dsn) { - return; - } - const { environment, release } = client.getOptions() || {}; - // TODO - the only reason we need the non-null assertion on `dsn.publicKey` (below) is because `dsn.publicKey` has - // to be optional while we transition from `dsn.user` -> `dsn.publicKey`. Once `dsn.user` is removed, we can make - // `dsn.publicKey` required and remove the `!`. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return computeTracestateValue({ trace_id: this.traceId, environment, release, public_key: dsn.publicKey! }); - } } diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 1623f3ed16d3..801c365763e5 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -117,7 +117,7 @@ describe('Span', () => { const headers = span.getTraceHeaders(); expect(headers['sentry-trace']).toEqual(span.toTraceparent()); - expect(headers.tracestate).toEqual(`sentry=${transaction.tracestate}`); + expect(headers.tracestate).toEqual(transaction.tracestate); }); }); From b4e8b8a3f622a3244928d8ac05956e4973a586c4 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:42:26 -0800 Subject: [PATCH 3/9] store sentry tracestate value in metadata object rather than as top-level property --- packages/tracing/src/span.ts | 2 +- packages/tracing/src/transaction.ts | 14 ++++++-------- packages/tracing/test/hub.test.ts | 15 +++++++++++---- packages/tracing/test/span.test.ts | 2 +- packages/types/src/transaction.ts | 5 ++--- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/tracing/src/span.ts b/packages/tracing/src/span.ts index f5ebcfedd46c..d83102f5bba8 100644 --- a/packages/tracing/src/span.ts +++ b/packages/tracing/src/span.ts @@ -291,7 +291,7 @@ export class Span implements SpanInterface { */ public getTraceHeaders(): TraceHeaders { // tracestates live on the transaction, so if this is a free-floating span, there won't be one - const tracestate = this.transaction && `sentry=${this.transaction.tracestate}`; // TODO kmclb + const tracestate = this.transaction && `sentry=${this.transaction.metadata?.tracestate?.sentry}`; return { 'sentry-trace': this.toTraceparent(), diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index a0dbb53a3072..d408db7efe13 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -15,9 +15,8 @@ import { Span as SpanClass, SpanRecorder } from './span'; export class Transaction extends SpanClass implements TransactionInterface { public name: string; - public readonly tracestate: string; - private _metadata: TransactionMetadata = {}; + public metadata: TransactionMetadata; private _measurements: Measurements = {}; @@ -46,9 +45,9 @@ export class Transaction extends SpanClass implements TransactionInterface { this._trimEnd = transactionContext.trimEnd; - // _getNewTracestate only returns undefined in the absence of a client or dsn, in which case it doesn't matter what - // the header values are - nothing can be sent anyway - so the third alternative here is just to make TS happy - this.tracestate = transactionContext.tracestate || this._getNewTracestate(this._hub) || 'things are broken'; + this.metadata = transactionContext.metadata || {}; + this.metadata.tracestate = this.metadata.tracestate || {}; + this.metadata.tracestate.sentry = this.metadata.tracestate.sentry || this._getNewTracestate(this._hub); // this is because transactions are also spans, and spans have a transaction pointer this.transaction = this; @@ -85,7 +84,7 @@ export class Transaction extends SpanClass implements TransactionInterface { * @hidden */ public setMetadata(newMetadata: TransactionMetadata): void { - this._metadata = { ...this._metadata, ...newMetadata }; + this.metadata = { ...this.metadata, ...newMetadata }; } /** @@ -122,7 +121,6 @@ export class Transaction extends SpanClass implements TransactionInterface { }).endTimestamp; } - this._metadata.tracestate = this.tracestate; const transaction: Event = { contexts: { @@ -134,7 +132,7 @@ export class Transaction extends SpanClass implements TransactionInterface { timestamp: this.endTimestamp, transaction: this.name, type: 'transaction', - debug_meta: this._metadata, + debug_meta: this.metadata, }; const hasMeasurements = Object.keys(this._measurements).length > 0; diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index f37da8d830e3..6d379254c227 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -35,12 +35,19 @@ describe('Hub', () => { name: 'dogpark', traceId: '12312012123120121231201212312012', parentSpanId: '1121201211212012', - tracestate: 'doGsaREgReaT', + metadata: { tracestate: { sentry: 'doGsaREgReaT' } }, }; const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); const transaction = hub.startTransaction(transactionContext); - expect(transaction).toEqual(expect.objectContaining(transactionContext)); + expect(transaction).toEqual( + expect.objectContaining({ + name: 'dogpark', + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + metadata: expect.objectContaining({ tracestate: { sentry: 'doGsaREgReaT' } }), + }), + ); }); it('creates a new tracestate value if not given one in transaction context', () => { @@ -62,7 +69,7 @@ describe('Hub', () => { public_key: 'dogsarebadatkeepingsecrets', }); - expect(transaction.tracestate).toEqual(b64Value); + expect(transaction.metadata?.tracestate?.sentry).toEqual(`sentry=${b64Value}`); }); it('uses default environment if none given', () => { @@ -80,7 +87,7 @@ describe('Hub', () => { public_key: 'dogsarebadatkeepingsecrets', }); - expect(transaction.tracestate).toEqual(b64Value); + expect(transaction.metadata?.tracestate?.sentry).toEqual(`sentry=${b64Value}`); }); }); diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 801c365763e5..b05c1bdd35a6 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -117,7 +117,7 @@ describe('Span', () => { const headers = span.getTraceHeaders(); expect(headers['sentry-trace']).toEqual(span.toTraceparent()); - expect(headers.tracestate).toEqual(transaction.tracestate); + expect(headers.tracestate).toEqual(transaction.metadata?.tracestate?.sentry); }); }); diff --git a/packages/types/src/transaction.ts b/packages/types/src/transaction.ts index f6669322cd7b..ee3fde1f9970 100644 --- a/packages/types/src/transaction.ts +++ b/packages/types/src/transaction.ts @@ -24,10 +24,9 @@ export interface TransactionContext extends SpanContext { parentSampled?: boolean; /** - * The tracestate header value associated with this transaction, potentially inherited from a parent transaction, - * which will be propagated across services to all child transactions + * Metadata associated with the transaction, for internal SDK use. */ - tracestate?: string; + metadata?: TransactionMetadata; } /** From 628d33ab516a9d36ba6d487afd21a851349ba1c0 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:47:03 -0800 Subject: [PATCH 4/9] let orphan spans get tracestate headers also --- packages/tracing/src/span.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/tracing/src/span.ts b/packages/tracing/src/span.ts index d83102f5bba8..5d58011cf352 100644 --- a/packages/tracing/src/span.ts +++ b/packages/tracing/src/span.ts @@ -291,7 +291,12 @@ export class Span implements SpanInterface { */ public getTraceHeaders(): TraceHeaders { // tracestates live on the transaction, so if this is a free-floating span, there won't be one - const tracestate = this.transaction && `sentry=${this.transaction.metadata?.tracestate?.sentry}`; + let tracestate; + if (this.transaction) { + tracestate = this.transaction.metadata?.tracestate?.sentry; + } else { + tracestate = this._getNewTracestate(); + } return { 'sentry-trace': this.toTraceparent(), From 2124f20067a1d2e1920e59c822b4b1a05c6184ee Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:50:28 -0800 Subject: [PATCH 5/9] fix envelope headers --- packages/core/src/request.ts | 9 +++++---- packages/core/test/lib/request.test.ts | 8 +++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index d7a36b5209a0..c73bf546d7e8 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -67,7 +67,7 @@ export function eventToSentryRequest(event: Event, api: API): SentryRequest { export function transactionToSentryRequest(event: Event, api: API): SentryRequest { const sdkInfo = getSdkMetadataForEnvelopeHeader(api); - const { transactionSampling, tracestate: encodedTracestate, ...metadata } = event.debug_meta || {}; + const { transactionSampling, tracestate, ...metadata } = event.debug_meta || {}; const { method: samplingMethod, rate: sampleRate } = transactionSampling || {}; if (Object.keys(metadata).length === 0) { delete event.debug_meta; @@ -77,9 +77,10 @@ export function transactionToSentryRequest(event: Event, api: API): SentryReques // the tracestate is stored in bas64-encoded JSON, but envelope header values are expected to be full JS values, // so we have to decode and reinflate it - let tracestate; + let reinflatedTracestate; try { - tracestate = JSON.parse(base64ToUnicode(encodedTracestate as string)); + const encodedSentryValue = (tracestate?.sentry as string).replace('sentry=', ''); + reinflatedTracestate = JSON.parse(base64ToUnicode(encodedSentryValue)); } catch (err) { logger.warn(err); } @@ -88,7 +89,7 @@ export function transactionToSentryRequest(event: Event, api: API): SentryReques event_id: event.event_id, sent_at: new Date().toISOString(), ...(sdkInfo && { sdk: sdkInfo }), - ...(tracestate && { trace: tracestate }), // trace context for dynamic sampling on relay + ...(reinflatedTracestate && { trace: reinflatedTracestate }), // trace context for dynamic sampling on relay }); const itemHeaders = JSON.stringify({ diff --git a/packages/core/test/lib/request.test.ts b/packages/core/test/lib/request.test.ts index abb4b8cf70e9..7f21ffe6b818 100644 --- a/packages/core/test/lib/request.test.ts +++ b/packages/core/test/lib/request.test.ts @@ -68,9 +68,11 @@ describe('eventToSentryRequest', () => { // release: 'off.leash.park', // public_key: 'dogsarebadatkeepingsecrets', // }), - tracestate: - 'eyJ0cmFjZV9pZCI6IjEyMzEyMDEyMTEyMTIwMTIiLCJlbnZpcm9ubWVudCI6ImRvZ3BhcmsiLCJyZWxlYXNlIjoib2ZmLmxlYXNo' + - 'LnBhcmsiLCJwdWJsaWNfa2V5IjoiZG9nc2FyZWJhZGF0a2VlcGluZ3NlY3JldHMifQ', + tracestate: { + sentry: + 'sentry=eyJ0cmFjZV9pZCI6IjEyMzEyMDEyMTEyMTIwMTIiLCJlbnZpcm9ubWVudCI6ImRvZ3BhcmsiLCJyZWxlYXNlIjoib2ZmLmxlYXNo' + + 'LnBhcmsiLCJwdWJsaWNfa2V5IjoiZG9nc2FyZWJhZGF0a2VlcGluZ3NlY3JldHMifQ', + }, }, spans: [], transaction: '/dogs/are/great/', From 1df7bfbbb547f1840d1edced3378987aa244cb0b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 20:52:59 -0800 Subject: [PATCH 6/9] clean up tracestate computation function --- packages/tracing/src/utils.ts | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/tracing/src/utils.ts b/packages/tracing/src/utils.ts index 1a722ac48e07..a6e32b728da9 100644 --- a/packages/tracing/src/utils.ts +++ b/packages/tracing/src/utils.ts @@ -68,22 +68,23 @@ export function secToMs(time: number): number { // so it can be used in manual instrumentation without necessitating a hard dependency on @sentry/utils export { stripUrlQueryAndFragment } from '@sentry/utils'; +type TracestateData = { + trace_id: string; + environment: string | undefined | null; + release: string | undefined | null; + public_key: string; +}; + /** - * Compute the value of a tracestate header. + * Compute the value of a Sentry tracestate header. * * @throws SentryError (because using the logger creates a circular dependency) * @returns the base64-encoded header value */ -// Note: this is here instead of in the tracing package since @sentry/core tests rely on it -export function computeTracestateValue(tracestateData: { - trace_id: string; - environment: string | undefined | null; - release: string | undefined | null; - public_key: string; -}): string { +export function computeTracestateValue(data: TracestateData): string { // `JSON.stringify` will drop keys with undefined values, but not ones with null values - tracestateData.environment = tracestateData.environment || null; - tracestateData.release = tracestateData.release || null; + data.environment = data.environment || null; + data.release = data.release || null; // See https://www.w3.org/TR/trace-context/#tracestate-header-field-values // The spec for tracestate header values calls for a string of the form @@ -94,8 +95,8 @@ export function computeTracestateValue(tracestateData: { // used to pad the end of base64 values though, so to avoid confusion, we strip them off. (Most languages' base64 // decoding functions (including those in JS) are able to function without the padding.) try { - return unicodeToBase64(JSON.stringify(tracestateData)).replace(/={1,2}$/, ''); + return unicodeToBase64(JSON.stringify(data)).replace(/={1,2}$/, ''); } catch (err) { - throw new SentryError(`[Tracing] Error creating tracestate header: ${err}`); + throw new SentryError(`[Tracing] Error computing tracestate value from data: ${err}\nData: ${data}`); } } From 1ee03f39326a9e695c0130e16ee1c6009127f3b7 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 21:20:44 -0800 Subject: [PATCH 7/9] fix linting issues --- packages/tracing/src/transaction.ts | 8 -------- packages/types/src/debugMeta.ts | 1 - 2 files changed, 9 deletions(-) diff --git a/packages/tracing/src/transaction.ts b/packages/tracing/src/transaction.ts index d408db7efe13..493036d1a3b2 100644 --- a/packages/tracing/src/transaction.ts +++ b/packages/tracing/src/transaction.ts @@ -10,12 +10,10 @@ import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils'; import { Span as SpanClass, SpanRecorder } from './span'; - /** JSDoc */ export class Transaction extends SpanClass implements TransactionInterface { public name: string; - public metadata: TransactionMetadata; private _measurements: Measurements = {}; @@ -121,7 +119,6 @@ export class Transaction extends SpanClass implements TransactionInterface { }).endTimestamp; } - const transaction: Event = { contexts: { trace: this.getTraceContext(), @@ -170,9 +167,4 @@ export class Transaction extends SpanClass implements TransactionInterface { return this; } - - - - - } diff --git a/packages/types/src/debugMeta.ts b/packages/types/src/debugMeta.ts index f28e7afad107..d9851ae11edd 100644 --- a/packages/types/src/debugMeta.ts +++ b/packages/types/src/debugMeta.ts @@ -5,7 +5,6 @@ import { TransactionMetadata } from './transaction'; **/ export type DebugMeta = { images?: Array; - } & TransactionMetadata; /** From 29a8e025ad427eb7453a02ed1f5033958e723f4b Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Tue, 16 Feb 2021 21:37:27 -0800 Subject: [PATCH 8/9] fix test --- packages/tracing/test/hub.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/tracing/test/hub.test.ts b/packages/tracing/test/hub.test.ts index 6d379254c227..065f44844e85 100644 --- a/packages/tracing/test/hub.test.ts +++ b/packages/tracing/test/hub.test.ts @@ -35,7 +35,7 @@ describe('Hub', () => { name: 'dogpark', traceId: '12312012123120121231201212312012', parentSpanId: '1121201211212012', - metadata: { tracestate: { sentry: 'doGsaREgReaT' } }, + metadata: { tracestate: { sentry: 'sentry=doGsaREgReaT' } }, }; const hub = new Hub(new BrowserClient({ tracesSampleRate: 1 })); const transaction = hub.startTransaction(transactionContext); @@ -45,7 +45,7 @@ describe('Hub', () => { name: 'dogpark', traceId: '12312012123120121231201212312012', parentSpanId: '1121201211212012', - metadata: expect.objectContaining({ tracestate: { sentry: 'doGsaREgReaT' } }), + metadata: expect.objectContaining({ tracestate: { sentry: 'sentry=doGsaREgReaT' } }), }), ); }); From dd450628aa24fa2f8096aab3e6fd5593cb57d735 Mon Sep 17 00:00:00 2001 From: Katie Byers Date: Wed, 17 Feb 2021 08:59:48 -0800 Subject: [PATCH 9/9] clarify that tracestate.sentry should exist --- packages/core/src/request.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/request.ts b/packages/core/src/request.ts index c73bf546d7e8..8558daf5d0bb 100644 --- a/packages/core/src/request.ts +++ b/packages/core/src/request.ts @@ -79,7 +79,11 @@ export function transactionToSentryRequest(event: Event, api: API): SentryReques // so we have to decode and reinflate it let reinflatedTracestate; try { - const encodedSentryValue = (tracestate?.sentry as string).replace('sentry=', ''); + // Because transaction metadata passes through a number of locations (transactionContext, transaction, event during + // processing, event as sent), each with different requirements, all of the parts are typed as optional. That said, + // if we get to this point and either `tracestate` or `tracestate.sentry` are undefined, something's gone very wrong. + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const encodedSentryValue = tracestate!.sentry!.replace('sentry=', ''); reinflatedTracestate = JSON.parse(base64ToUnicode(encodedSentryValue)); } catch (err) { logger.warn(err);