diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0b8138afb33c..a5523a861eee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -661,7 +661,7 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_get_metadata.outputs.changed_node == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-20.04 - timeout-minutes: 10 + timeout-minutes: 15 strategy: fail-fast: false matrix: diff --git a/packages/nextjs/test/integration/sentry.client.config.js b/packages/nextjs/test/integration/sentry.client.config.js index e8da7a90914b..dbb6e760ea8d 100644 --- a/packages/nextjs/test/integration/sentry.client.config.js +++ b/packages/nextjs/test/integration/sentry.client.config.js @@ -3,7 +3,7 @@ import { Integrations } from '@sentry/tracing'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampler: () => true, debug: process.env.SDK_DEBUG, integrations: [ diff --git a/packages/nextjs/test/integration/sentry.edge.config.js b/packages/nextjs/test/integration/sentry.edge.config.js index 8d83922b637d..36600e702048 100644 --- a/packages/nextjs/test/integration/sentry.edge.config.js +++ b/packages/nextjs/test/integration/sentry.edge.config.js @@ -2,6 +2,7 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampleRate: 1.0, + tracePropagationTargets: ['http://example.com'], debug: process.env.SDK_DEBUG, }); diff --git a/packages/nextjs/test/integration/sentry.server.config.js b/packages/nextjs/test/integration/sentry.server.config.js index da0759f4475e..54c5db73a1a2 100644 --- a/packages/nextjs/test/integration/sentry.server.config.js +++ b/packages/nextjs/test/integration/sentry.server.config.js @@ -2,7 +2,8 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', - tracesSampleRate: 1, + tracesSampleRate: 1.0, + tracePropagationTargets: ['http://example.com'], debug: process.env.SDK_DEBUG, integrations: defaults => [ diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts index f582288f8314..750f49b40210 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/server.ts @@ -12,6 +12,7 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts index 6674e2a1bb77..93738d4c0a12 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts index 86cfa033b79e..867bb0e6131e 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts index 78e24ffa9f10..21738f3b3fb8 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/server.ts @@ -12,6 +12,8 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + // disable requests to /express + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/sentry-trace/server.ts b/packages/node-integration-tests/suites/express/sentry-trace/server.ts index 76bb399e3bc0..1cfc02648f02 100644 --- a/packages/node-integration-tests/suites/express/sentry-trace/server.ts +++ b/packages/node-integration-tests/suites/express/sentry-trace/server.ts @@ -12,6 +12,7 @@ Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', environment: 'prod', + tracePropagationTargets: [/^(?!.*express).*$/], // eslint-disable-next-line deprecation/deprecation integrations: [new Sentry.Integrations.Http({ tracing: true }), new Tracing.Integrations.Express({ app })], tracesSampleRate: 1.0, diff --git a/packages/node-integration-tests/suites/express/tracing/server.ts b/packages/node-integration-tests/suites/express/tracing/server.ts index e857621ad22e..1c56a81fef98 100644 --- a/packages/node-integration-tests/suites/express/tracing/server.ts +++ b/packages/node-integration-tests/suites/express/tracing/server.ts @@ -7,6 +7,8 @@ const app = express(); Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', release: '1.0', + // disable attaching headers to /test/* endpoints + tracePropagationTargets: [/^(?!.*test).*$/], integrations: [new Sentry.Integrations.Http({ tracing: true }), new Sentry.Integrations.Express({ app })], tracesSampleRate: 1.0, }); diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index cde2cb745cd9..88120853ee69 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -202,11 +202,11 @@ export class TestEnv { */ public async getAPIResponse( url?: string, - headers?: Record, + headers: Record = {}, endServer: boolean = true, ): Promise { try { - const { data } = await axios.get(url || this.url, { headers: headers || {} }); + const { data } = await axios.get(url || this.url, { headers }); return data; } finally { await Sentry.flush(); diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index 54d761861348..b210500e90e0 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -1,14 +1,26 @@ import type { Hub } from '@sentry/core'; -import { getCurrentHub } from '@sentry/core'; -import type { EventProcessor, Integration, SanitizedRequestData, Span, TracePropagationTargets } from '@sentry/types'; -import { dynamicSamplingContextToSentryBaggageHeader, fill, logger, stringMatchesSomePattern } from '@sentry/utils'; +import { getCurrentHub, getDynamicSamplingContextFromClient } from '@sentry/core'; +import type { + DynamicSamplingContext, + EventProcessor, + Integration, + SanitizedRequestData, + TracePropagationTargets, +} from '@sentry/types'; +import { + dynamicSamplingContextToSentryBaggageHeader, + fill, + generateSentryTraceHeader, + logger, + stringMatchesSomePattern, +} from '@sentry/utils'; import type * as http from 'http'; import type * as https from 'https'; import { LRUMap } from 'lru_map'; import type { NodeClient } from '../client'; import { NODE_VERSION } from '../nodeVersion'; -import type { RequestMethod, RequestMethodArgs } from './utils/http'; +import type { RequestMethod, RequestMethodArgs, RequestOptions } from './utils/http'; import { cleanSpanDescription, extractRawUrl, extractUrl, isSentryRequest, normalizeRequestArgs } from './utils/http'; interface TracingOptions { @@ -96,13 +108,20 @@ export class Http implements Integration { return; } - // TODO (v8): `tracePropagationTargets` and `shouldCreateSpanForRequest` will be removed from clientOptions - // and we will no longer have to do this optional merge, we can just pass `this._tracing` directly. - const tracingOptions = this._tracing ? { ...clientOptions, ...this._tracing } : undefined; + const shouldCreateSpanForRequest = + // eslint-disable-next-line deprecation/deprecation + this._tracing?.shouldCreateSpanForRequest || clientOptions?.shouldCreateSpanForRequest; + // eslint-disable-next-line deprecation/deprecation + const tracePropagationTargets = clientOptions?.tracePropagationTargets || this._tracing?.tracePropagationTargets; // eslint-disable-next-line @typescript-eslint/no-var-requires const httpModule = require('http'); - const wrappedHttpHandlerMaker = _createWrappedRequestMethodFactory(this._breadcrumbs, tracingOptions, httpModule); + const wrappedHttpHandlerMaker = _createWrappedRequestMethodFactory( + httpModule, + this._breadcrumbs, + shouldCreateSpanForRequest, + tracePropagationTargets, + ); fill(httpModule, 'get', wrappedHttpHandlerMaker); fill(httpModule, 'request', wrappedHttpHandlerMaker); @@ -113,9 +132,10 @@ export class Http implements Integration { // eslint-disable-next-line @typescript-eslint/no-var-requires const httpsModule = require('https'); const wrappedHttpsHandlerMaker = _createWrappedRequestMethodFactory( - this._breadcrumbs, - tracingOptions, httpsModule, + this._breadcrumbs, + shouldCreateSpanForRequest, + tracePropagationTargets, ); fill(httpsModule, 'get', wrappedHttpsHandlerMaker); fill(httpsModule, 'request', wrappedHttpsHandlerMaker); @@ -138,16 +158,17 @@ type WrappedRequestMethodFactory = (original: OriginalRequestMethod) => WrappedR * @returns A function which accepts the exiting handler and returns a wrapped handler */ function _createWrappedRequestMethodFactory( - breadcrumbsEnabled: boolean, - tracingOptions: TracingOptions | undefined, httpModule: typeof http | typeof https, + breadcrumbsEnabled: boolean, + shouldCreateSpanForRequest: ((url: string) => boolean) | undefined, + tracePropagationTargets: TracePropagationTargets | undefined, ): WrappedRequestMethodFactory { // We're caching results so we don't have to recompute regexp every time we create a request. const createSpanUrlMap = new LRUMap(100); const headersUrlMap = new LRUMap(100); const shouldCreateSpan = (url: string): boolean => { - if (tracingOptions?.shouldCreateSpanForRequest === undefined) { + if (shouldCreateSpanForRequest === undefined) { return true; } @@ -156,14 +177,13 @@ function _createWrappedRequestMethodFactory( return cachedDecision; } - const decision = tracingOptions.shouldCreateSpanForRequest(url); + const decision = shouldCreateSpanForRequest(url); createSpanUrlMap.set(url, decision); return decision; }; const shouldAttachTraceData = (url: string): boolean => { - // eslint-disable-next-line deprecation/deprecation - if (tracingOptions?.tracePropagationTargets === undefined) { + if (tracePropagationTargets === undefined) { return true; } @@ -172,12 +192,41 @@ function _createWrappedRequestMethodFactory( return cachedDecision; } - // eslint-disable-next-line deprecation/deprecation - const decision = stringMatchesSomePattern(url, tracingOptions.tracePropagationTargets); + const decision = stringMatchesSomePattern(url, tracePropagationTargets); headersUrlMap.set(url, decision); return decision; }; + /** + * Captures Breadcrumb based on provided request/response pair + */ + function addRequestBreadcrumb( + event: string, + requestSpanData: SanitizedRequestData, + req: http.ClientRequest, + res?: http.IncomingMessage, + ): void { + if (!getCurrentHub().getIntegration(Http)) { + return; + } + + getCurrentHub().addBreadcrumb( + { + category: 'http', + data: { + status_code: res && res.statusCode, + ...requestSpanData, + }, + type: 'http', + }, + { + event, + request: req, + response: res, + }, + ); + } + return function wrappedRequestMethodFactory(originalRequestMethod: OriginalRequestMethod): WrappedRequestMethod { return function wrappedMethod(this: unknown, ...args: RequestMethodArgs): http.ClientRequest { const requestArgs = normalizeRequestArgs(httpModule, args); @@ -191,74 +240,38 @@ function _createWrappedRequestMethodFactory( return originalRequestMethod.apply(httpModule, requestArgs); } - let requestSpan: Span | undefined; - const parentSpan = getCurrentHub().getScope().getSpan(); - - const method = requestOptions.method || 'GET'; - const requestSpanData: SanitizedRequestData = { - url: requestUrl, - 'http.method': method, - }; - if (requestOptions.hash) { - // strip leading "#" - requestSpanData['http.fragment'] = requestOptions.hash.substring(1); - } - if (requestOptions.search) { - // strip leading "?" - requestSpanData['http.query'] = requestOptions.search.substring(1); - } + const hub = getCurrentHub(); + const scope = hub.getScope(); + const parentSpan = scope.getSpan(); - if (tracingOptions && shouldCreateSpan(rawRequestUrl)) { - if (parentSpan) { - requestSpan = parentSpan.startChild({ - description: `${method} ${requestSpanData.url}`, + const data = getRequestSpanData(requestUrl, requestOptions); + + const requestSpan = shouldCreateSpan(rawRequestUrl) + ? parentSpan?.startChild({ op: 'http.client', - data: requestSpanData, - }); - - if (shouldAttachTraceData(rawRequestUrl)) { - const sentryTraceHeader = requestSpan.toTraceparent(); - __DEBUG_BUILD__ && - logger.log( - `[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to "${requestUrl}": `, - ); - - requestOptions.headers = { - ...requestOptions.headers, - 'sentry-trace': sentryTraceHeader, - }; - - if (parentSpan.transaction) { - const dynamicSamplingContext = parentSpan.transaction.getDynamicSamplingContext(); - const sentryBaggageHeader = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); - - let newBaggageHeaderField; - if (!requestOptions.headers || !requestOptions.headers.baggage) { - newBaggageHeaderField = sentryBaggageHeader; - } else if (!sentryBaggageHeader) { - newBaggageHeaderField = requestOptions.headers.baggage; - } else if (Array.isArray(requestOptions.headers.baggage)) { - newBaggageHeaderField = [...requestOptions.headers.baggage, sentryBaggageHeader]; - } else { - // Type-cast explanation: - // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity - // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this. - newBaggageHeaderField = [requestOptions.headers.baggage, sentryBaggageHeader] as string[]; - } - - requestOptions.headers = { - ...requestOptions.headers, - // Setting a hader to `undefined` will crash in node so we only set the baggage header when it's defined - ...(newBaggageHeaderField && { baggage: newBaggageHeaderField }), - }; - } - } else { - __DEBUG_BUILD__ && - logger.log( - `[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`, - ); - } + description: `${data['http.method']} ${data.url}`, + data, + }) + : undefined; + + if (shouldAttachTraceData(rawRequestUrl)) { + if (requestSpan) { + const sentryTraceHeader = requestSpan.toTraceparent(); + const dynamicSamplingContext = requestSpan?.transaction?.getDynamicSamplingContext(); + addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); + } else { + const client = hub.getClient(); + const { traceId, sampled, dsc } = scope.getPropagationContext(); + const sentryTraceHeader = generateSentryTraceHeader(traceId, undefined, sampled); + const dynamicSamplingContext = + dsc || (client ? getDynamicSamplingContextFromClient(traceId, client, scope) : undefined); + addHeadersToRequestOptions(requestOptions, requestUrl, sentryTraceHeader, dynamicSamplingContext); } + } else { + __DEBUG_BUILD__ && + logger.log( + `[Tracing] Not adding sentry-trace header to outgoing request (${requestUrl}) due to mismatching tracePropagationTargets option.`, + ); } // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access @@ -268,7 +281,7 @@ function _createWrappedRequestMethodFactory( // eslint-disable-next-line @typescript-eslint/no-this-alias const req = this; if (breadcrumbsEnabled) { - addRequestBreadcrumb('response', requestSpanData, req, res); + addRequestBreadcrumb('response', data, req, res); } if (requestSpan) { if (res.statusCode) { @@ -283,7 +296,7 @@ function _createWrappedRequestMethodFactory( const req = this; if (breadcrumbsEnabled) { - addRequestBreadcrumb('error', requestSpanData, req); + addRequestBreadcrumb('error', data, req); } if (requestSpan) { requestSpan.setHttpStatus(500); @@ -295,32 +308,55 @@ function _createWrappedRequestMethodFactory( }; } -/** - * Captures Breadcrumb based on provided request/response pair - */ -function addRequestBreadcrumb( - event: string, - requestSpanData: SanitizedRequestData, - req: http.ClientRequest, - res?: http.IncomingMessage, +function addHeadersToRequestOptions( + requestOptions: RequestOptions, + requestUrl: string, + sentryTraceHeader: string, + dynamicSamplingContext: Partial | undefined, ): void { - if (!getCurrentHub().getIntegration(Http)) { - return; + __DEBUG_BUILD__ && + logger.log(`[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to "${requestUrl}": `); + const sentryBaggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext); + const sentryBaggageHeader = normalizeBaggageHeader(requestOptions, sentryBaggage); + requestOptions.headers = { + ...requestOptions.headers, + 'sentry-trace': sentryTraceHeader, + // Setting a header to `undefined` will crash in node so we only set the baggage header when it's defined + ...(sentryBaggageHeader && { baggage: sentryBaggageHeader }), + }; +} + +function getRequestSpanData(requestUrl: string, requestOptions: RequestOptions): SanitizedRequestData { + const method = requestOptions.method || 'GET'; + const data: SanitizedRequestData = { + url: requestUrl, + 'http.method': method, + }; + if (requestOptions.hash) { + // strip leading "#" + data['http.fragment'] = requestOptions.hash.substring(1); + } + if (requestOptions.search) { + // strip leading "?" + data['http.query'] = requestOptions.search.substring(1); } + return data; +} - getCurrentHub().addBreadcrumb( - { - category: 'http', - data: { - status_code: res && res.statusCode, - ...requestSpanData, - }, - type: 'http', - }, - { - event, - request: req, - response: res, - }, - ); +function normalizeBaggageHeader( + requestOptions: RequestOptions, + sentryBaggageHeader: string | undefined, +): string | string[] | undefined { + if (!requestOptions.headers || !requestOptions.headers.baggage) { + return sentryBaggageHeader; + } else if (!sentryBaggageHeader) { + return requestOptions.headers.baggage as string | string[]; + } else if (Array.isArray(requestOptions.headers.baggage)) { + return [...requestOptions.headers.baggage, sentryBaggageHeader]; + } + + // Type-cast explanation: + // Technically this the following could be of type `(number | string)[]` but for the sake of simplicity + // we say this is undefined behaviour, since it would not be baggage spec conform if the user did this. + return [requestOptions.headers.baggage, sentryBaggageHeader] as string[]; } diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index b4d4734e88bd..099a46e1a356 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -54,6 +54,21 @@ describe('tracing', () => { return transaction; } + function getHub(customOptions: Partial = {}) { + const options = getDefaultNodeClientOptions({ + dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012', + tracesSampleRate: 1.0, + integrations: [new HttpIntegration({ tracing: true })], + release: '1.0.0', + environment: 'production', + ...customOptions, + }); + const hub = new Hub(new NodeClient(options)); + jest.spyOn(sentryCore, 'getCurrentHub').mockReturnValue(hub); + + return hub; + } + it("creates a span for each outgoing non-sentry request when there's a transaction on the scope", () => { nock('http://dogs.are.great').get('/').reply(200); @@ -162,6 +177,51 @@ describe('tracing', () => { ]); }); + it('generates and uses propagation context to attach baggage and sentry-trace header', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + const request = http.get('http://dogs.are.great/'); + const sentryTraceHeader = request.getHeader('sentry-trace') as string; + const baggageHeader = request.getHeader('baggage') as string; + + const parts = sentryTraceHeader.split('-'); + expect(parts.length).toEqual(3); + expect(parts[0]).toEqual('12312012123120121231201212312012'); + expect(parts[1]).toEqual(expect.any(String)); + expect(parts[2]).toEqual('1'); + + expect(baggageHeader).toEqual( + 'sentry-environment=production,sentry-release=1.0.0,sentry-user_segment=segmentA,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=12312012123120121231201212312012,sentry-sample_rate=1', + ); + }); + + it('uses incoming propagation context to attach baggage and sentry-trace', async () => { + nock('http://dogs.are.great').get('/').reply(200); + + const hub = getHub(); + hub.getScope().setPropagationContext({ + traceId: '86f39e84263a4de99c326acab3bfe3bd', + spanId: '86f39e84263a4de9', + sampled: true, + dsc: { + trace_id: '86f39e84263a4de99c326acab3bfe3bd', + public_key: 'test-public-key', + }, + }); + + const request = http.get('http://dogs.are.great/'); + const sentryTraceHeader = request.getHeader('sentry-trace') as string; + const baggageHeader = request.getHeader('baggage') as string; + + const parts = sentryTraceHeader.split('-'); + expect(parts.length).toEqual(3); + expect(parts[0]).toEqual('86f39e84263a4de99c326acab3bfe3bd'); + expect(parts[1]).toEqual(expect.any(String)); + expect(parts[2]).toEqual('1'); + + expect(baggageHeader).toEqual('sentry-trace_id=86f39e84263a4de99c326acab3bfe3bd,sentry-public_key=test-public-key'); + }); + it("doesn't attach the sentry-trace header to outgoing sentry requests", () => { nock('http://squirrelchasers.ingest.sentry.io').get('/api/12312012/store/').reply(200); @@ -270,9 +330,8 @@ describe('tracing', () => { return transaction; } - // TODO (v8): These can be removed once we remove these properties from client options describe('as client options', () => { - it("doesn't create span if shouldCreateSpanForRequest returns false", () => { + it('creates span with propagation context if shouldCreateSpanForRequest returns false', () => { const url = 'http://dogs.are.great/api/v1/index/'; nock(url).get(/.*/).reply(200); @@ -295,8 +354,15 @@ describe('tracing', () => { expect(httpSpans.length).toBe(0); // And headers are not attached without span creation - expect(request.getHeader('sentry-trace')).toBeUndefined(); - expect(request.getHeader('baggage')).toBeUndefined(); + expect(request.getHeader('sentry-trace')).toBeDefined(); + expect(request.getHeader('baggage')).toBeDefined(); + + const propagationContext = hub.getScope().getPropagationContext(); + + expect((request.getHeader('sentry-trace') as string).includes(propagationContext.traceId)).toBe(true); + expect(request.getHeader('baggage')).toEqual( + `sentry-environment=production,sentry-release=1.0.0,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=${propagationContext.traceId}`, + ); }); it.each([ @@ -366,7 +432,7 @@ describe('tracing', () => { }); describe('as Http integration constructor options', () => { - it("doesn't create span if shouldCreateSpanForRequest returns false", () => { + it('creates span with propagation context if shouldCreateSpanForRequest returns false', () => { const url = 'http://dogs.are.great/api/v1/index/'; nock(url).get(/.*/).reply(200); @@ -393,8 +459,15 @@ describe('tracing', () => { expect(httpSpans.length).toBe(0); // And headers are not attached without span creation - expect(request.getHeader('sentry-trace')).toBeUndefined(); - expect(request.getHeader('baggage')).toBeUndefined(); + expect(request.getHeader('sentry-trace')).toBeDefined(); + expect(request.getHeader('baggage')).toBeDefined(); + + const propagationContext = hub.getScope().getPropagationContext(); + + expect((request.getHeader('sentry-trace') as string).includes(propagationContext.traceId)).toBe(true); + expect(request.getHeader('baggage')).toEqual( + `sentry-environment=production,sentry-release=1.0.0,sentry-public_key=dogsarebadatkeepingsecrets,sentry-trace_id=${propagationContext.traceId}`, + ); }); it.each([ diff --git a/packages/remix/test/integration/app_v1/entry.server.tsx b/packages/remix/test/integration/app_v1/entry.server.tsx index ae879492e236..d48f2644fac4 100644 --- a/packages/remix/test/integration/app_v1/entry.server.tsx +++ b/packages/remix/test/integration/app_v1/entry.server.tsx @@ -6,6 +6,7 @@ import * as Sentry from '@sentry/remix'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1, + tracePropagationTargets: ['example.org'], // Disabling to test series of envelopes deterministically. autoSessionTracking: false, }); diff --git a/packages/remix/test/integration/app_v2/entry.server.tsx b/packages/remix/test/integration/app_v2/entry.server.tsx index ae879492e236..d48f2644fac4 100644 --- a/packages/remix/test/integration/app_v2/entry.server.tsx +++ b/packages/remix/test/integration/app_v2/entry.server.tsx @@ -6,6 +6,7 @@ import * as Sentry from '@sentry/remix'; Sentry.init({ dsn: 'https://public@dsn.ingest.sentry.io/1337', tracesSampleRate: 1, + tracePropagationTargets: ['example.org'], // Disabling to test series of envelopes deterministically. autoSessionTracking: false, });