|
1 | 1 | import { getCurrentHub, Hub } from '@sentry/hub'; |
2 | | -import { Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; |
| 2 | +import { DebugMeta, Event, Measurements, Transaction as TransactionInterface, TransactionContext } from '@sentry/types'; |
3 | 3 | import { dropUndefinedKeys, isInstanceOf, logger } from '@sentry/utils'; |
4 | 4 |
|
5 | 5 | import { Span as SpanClass, SpanRecorder } from './span'; |
| 6 | +import { computeTracestateValue } from './utils'; |
6 | 7 |
|
7 | | -interface TransactionMetadata { |
8 | | - transactionSampling?: { [key: string]: string | number }; |
9 | | -} |
| 8 | +type TransactionMetadata = Pick<DebugMeta, 'transactionSampling' | 'tracestate'>; |
10 | 9 |
|
11 | 10 | /** JSDoc */ |
12 | 11 | export class Transaction extends SpanClass implements TransactionInterface { |
13 | 12 | public name: string; |
14 | 13 |
|
| 14 | + public readonly tracestate: string; |
| 15 | + |
15 | 16 | private _metadata: TransactionMetadata = {}; |
16 | 17 |
|
17 | 18 | private _measurements: Measurements = {}; |
@@ -41,6 +42,10 @@ export class Transaction extends SpanClass implements TransactionInterface { |
41 | 42 |
|
42 | 43 | this._trimEnd = transactionContext.trimEnd; |
43 | 44 |
|
| 45 | + // _getNewTracestate only returns undefined in the absence of a client or dsn, in which case it doesn't matter what |
| 46 | + // the header values are - nothing can be sent anyway - so the third alternative here is just to make TS happy |
| 47 | + this.tracestate = transactionContext.tracestate || this._getNewTracestate() || 'things are broken'; |
| 48 | + |
44 | 49 | // this is because transactions are also spans, and spans have a transaction pointer |
45 | 50 | this.transaction = this; |
46 | 51 | } |
@@ -113,6 +118,8 @@ export class Transaction extends SpanClass implements TransactionInterface { |
113 | 118 | }).endTimestamp; |
114 | 119 | } |
115 | 120 |
|
| 121 | + this._metadata.tracestate = this.tracestate; |
| 122 | + |
116 | 123 | const transaction: Event = { |
117 | 124 | contexts: { |
118 | 125 | trace: this.getTraceContext(), |
@@ -161,4 +168,27 @@ export class Transaction extends SpanClass implements TransactionInterface { |
161 | 168 |
|
162 | 169 | return this; |
163 | 170 | } |
| 171 | + |
| 172 | + /** |
| 173 | + * Create a new tracestate header value |
| 174 | + * |
| 175 | + * @returns The new tracestate value, or undefined if there's no client or no dsn |
| 176 | + */ |
| 177 | + private _getNewTracestate(): string | undefined { |
| 178 | + const client = this._hub.getClient(); |
| 179 | + const dsn = client?.getDsn(); |
| 180 | + |
| 181 | + if (!client || !dsn) { |
| 182 | + return; |
| 183 | + } |
| 184 | + |
| 185 | + const { environment, release } = client.getOptions() || {}; |
| 186 | + |
| 187 | + // TODO - the only reason we need the non-null assertion on `dsn.publicKey` (below) is because `dsn.publicKey` has |
| 188 | + // to be optional while we transition from `dsn.user` -> `dsn.publicKey`. Once `dsn.user` is removed, we can make |
| 189 | + // `dsn.publicKey` required and remove the `!`. |
| 190 | + |
| 191 | + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
| 192 | + return computeTracestateValue({ trace_id: this.traceId, environment, release, public_key: dsn.publicKey! }); |
| 193 | + } |
164 | 194 | } |
0 commit comments