From e86eeac2bd0791af444546fcc93506b32af26e32 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 16 Apr 2024 16:49:13 +0200 Subject: [PATCH 1/6] feat(core): Add `server.address` to browser `http.client` spans --- packages/browser/src/tracing/request.ts | 7 ++++++- packages/core/src/fetch.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index 2c013fe27232..f85282b6a8a4 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -4,6 +4,7 @@ import { addXhrInstrumentationHandler, } from '@sentry-internal/browser-utils'; import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SentryNonRecordingSpan, getActiveSpan, @@ -26,6 +27,7 @@ import { browserPerformanceTimeOrigin, dynamicSamplingContextToSentryBaggageHeader, generateSentryTraceHeader, + parseUrl, stringMatchesSomePattern, } from '@sentry/utils'; import { WINDOW } from '../helpers'; @@ -310,6 +312,8 @@ export function xhrCallback( const hasParent = !!getActiveSpan(); + const parsedUrl = parseUrl(sentryXhrData.url); + const span = shouldCreateSpanResult && hasParent ? startInactiveSpan({ @@ -318,9 +322,10 @@ export function xhrCallback( type: 'xhr', 'http.method': sentryXhrData.method, url: sentryXhrData.url, + 'server.address': parsedUrl.host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client', }, - op: 'http.client', }) : new SentryNonRecordingSpan(); diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts index f9069b6b6efa..784a790c25fb 100644 --- a/packages/core/src/fetch.ts +++ b/packages/core/src/fetch.ts @@ -4,9 +4,10 @@ import { dynamicSamplingContextToSentryBaggageHeader, generateSentryTraceHeader, isInstanceOf, + parseUrl, } from '@sentry/utils'; import { getClient, getCurrentScope, getIsolationScope } from './currentScopes'; -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes'; +import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes'; import { SPAN_STATUS_ERROR, getDynamicSamplingContextFromClient, @@ -83,6 +84,8 @@ export function instrumentFetchRequest( const hasParent = !!getActiveSpan(); + const parsedUrl = parseUrl(url); + const span = shouldCreateSpanResult && hasParent ? startInactiveSpan({ @@ -91,9 +94,10 @@ export function instrumentFetchRequest( url, type: 'fetch', 'http.method': method, + 'server.address': parsedUrl.host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin, + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client', }, - op: 'http.client', }) : new SentryNonRecordingSpan(); From c4a51422ef7af0ef59d2354352a11d0338361d07 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 16 Apr 2024 17:15:02 +0200 Subject: [PATCH 2/6] add tests --- .../suites/tracing/request/fetch/test.ts | 7 ++++ .../suites/tracing/request/xhr/test.ts | 7 ++++ packages/browser/src/tracing/request.ts | 1 + packages/core/src/fetch.ts | 1 + .../{next.config.js => next.config.js.bak} | 0 packages/nextjs/test/integration/package.json | 2 +- .../nextjs/test/integration/package.json.bak | 41 +++++++++++++++++++ .../test/client/tracingFetch.test.ts | 2 + 8 files changed, 60 insertions(+), 1 deletion(-) rename packages/nextjs/test/integration/{next.config.js => next.config.js.bak} (100%) create mode 100644 packages/nextjs/test/integration/package.json.bak diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/fetch/test.ts index 00cf0baafc6a..de6b1521d686 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/fetch/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch/test.ts @@ -36,6 +36,13 @@ sentryTest('should create spans for fetch requests', async ({ getLocalTestPath, start_timestamp: expect.any(Number), timestamp: expect.any(Number), trace_id: tracingEvent.contexts?.trace?.trace_id, + data: { + 'http.method': 'GET', + 'http.url': `http://example.com/${index}`, + url: `http://example.com/${index}`, + 'server.address': 'example.com', + type: 'fetch', + }, }), ); }); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/xhr/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/xhr/test.ts index 13646a34826e..5dbfd3edf4cb 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/request/xhr/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/request/xhr/test.ts @@ -24,6 +24,13 @@ sentryTest('should create spans for XHR requests', async ({ getLocalTestPath, pa start_timestamp: expect.any(Number), timestamp: expect.any(Number), trace_id: eventData.contexts?.trace?.trace_id, + data: { + 'http.method': 'GET', + 'http.url': `http://example.com/${index}`, + url: `http://example.com/${index}`, + 'server.address': 'example.com', + type: 'xhr', + }, }), ); }); diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index f85282b6a8a4..d99b2cf85536 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -321,6 +321,7 @@ export function xhrCallback( attributes: { type: 'xhr', 'http.method': sentryXhrData.method, + 'http.url': sentryXhrData.url, url: sentryXhrData.url, 'server.address': parsedUrl.host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser', diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts index 784a790c25fb..8d102fac63a7 100644 --- a/packages/core/src/fetch.ts +++ b/packages/core/src/fetch.ts @@ -94,6 +94,7 @@ export function instrumentFetchRequest( url, type: 'fetch', 'http.method': method, + 'http.url': url, 'server.address': parsedUrl.host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client', diff --git a/packages/nextjs/test/integration/next.config.js b/packages/nextjs/test/integration/next.config.js.bak similarity index 100% rename from packages/nextjs/test/integration/next.config.js rename to packages/nextjs/test/integration/next.config.js.bak diff --git a/packages/nextjs/test/integration/package.json b/packages/nextjs/test/integration/package.json index f4c547b5b687..a38da23b50f8 100644 --- a/packages/nextjs/test/integration/package.json +++ b/packages/nextjs/test/integration/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@sentry/nextjs": "file:../../", - "next": "latest", + "next": "13.2.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/nextjs/test/integration/package.json.bak b/packages/nextjs/test/integration/package.json.bak new file mode 100644 index 000000000000..f4c547b5b687 --- /dev/null +++ b/packages/nextjs/test/integration/package.json.bak @@ -0,0 +1,41 @@ +{ + "name": "with-typescript", + "license": "MIT", + "scripts": { + "dev": "next", + "build": "next build", + "predebug": "source ../integration_test_utils.sh && link_monorepo_packages '../../..' && yarn build", + "start": "next start", + "pretest": "yarn build", + "test:client": "playwright test", + "test:server": "jest --forceExit --runInBand" + }, + "dependencies": { + "@sentry/nextjs": "file:../../", + "next": "latest", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@types/node": "^15.3.1", + "@types/react": "17.0.47", + "@types/react-dom": "17.0.17", + "nock": "^13.1.0", + "typescript": "4.9.5", + "yargs": "^16.2.0" + }, + "resolutions": { + "@sentry/browser": "file:../../../browser", + "@sentry/core": "file:../../../core", + "@sentry/node": "file:../../../node", + "@sentry/opentelemetry": "file:../../../opentelemetry", + "@sentry/react": "file:../../../react", + "@sentry-internal/browser-utils": "file:../../../browser-utils", + "@sentry-internal/replay": "file:../../../replay-internal", + "@sentry-internal/replay-canvas": "file:../../../replay-canvas", + "@sentry-internal/feedback": "file:../../../feedback", + "@sentry/types": "file:../../../types", + "@sentry/utils": "file:../../../utils", + "@sentry/vercel-edge": "file:../../../vercel-edge" + } +} diff --git a/packages/nextjs/test/integration/test/client/tracingFetch.test.ts b/packages/nextjs/test/integration/test/client/tracingFetch.test.ts index 8517b4ab0fce..395b72637354 100644 --- a/packages/nextjs/test/integration/test/client/tracingFetch.test.ts +++ b/packages/nextjs/test/integration/test/client/tracingFetch.test.ts @@ -33,6 +33,8 @@ test('should correctly instrument `fetch` for performance tracing', async ({ pag data: { 'http.method': 'GET', url: 'http://example.com', + 'http.url': 'http://example.com', + 'server.address': 'example.com', type: 'fetch', 'http.response_content_length': expect.any(Number), 'http.response.status_code': 200, From b8187eaa3f4d3ef2d88696104d50bded7d8acb6f Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 16 Apr 2024 17:48:02 +0200 Subject: [PATCH 3/6] fix it & tests --- .../request/fetch-relative-url/init.js | 9 +++ .../request/fetch-relative-url/subject.js | 3 + .../request/fetch-relative-url/test.ts | 79 +++++++++++++++++++ .../tracing/request/xhr-relative-url/init.js | 9 +++ .../request/xhr-relative-url/subject.js | 12 +++ .../tracing/request/xhr-relative-url/test.ts | 79 +++++++++++++++++++ packages/browser/src/tracing/request.ts | 30 ++++++- packages/core/src/fetch.ts | 52 +++++++----- 8 files changed, 251 insertions(+), 22 deletions(-) create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/test.ts create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/init.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/subject.js create mode 100644 dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/test.ts diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/init.js b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/init.js new file mode 100644 index 000000000000..83076460599f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration()], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/subject.js b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/subject.js new file mode 100644 index 000000000000..b0d4abc78c65 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/subject.js @@ -0,0 +1,3 @@ +fetch('/test-req/0').then( + fetch('/test-req/1', { headers: { 'X-Test-Header': 'existing-header' } }).then(fetch('/test-req/2')), +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/test.ts new file mode 100644 index 000000000000..d4a6065a177b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/fetch-relative-url/test.ts @@ -0,0 +1,79 @@ +import { expect } from '@playwright/test'; + +import { TEST_HOST, sentryTest } from '../../../../utils/fixtures'; +import { + envelopeRequestParser, + shouldSkipTracingTest, + waitForTransactionRequestOnUrl, +} from '../../../../utils/helpers'; + +sentryTest('should create spans for fetch requests', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForTransactionRequestOnUrl(page, url); + const tracingEvent = envelopeRequestParser(req); + + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); + + expect(requestSpans).toHaveLength(3); + + requestSpans?.forEach((span, index) => + expect(span).toMatchObject({ + description: `GET /test-req/${index}`, + parent_span_id: tracingEvent.contexts?.trace?.span_id, + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: tracingEvent.contexts?.trace?.trace_id, + data: { + 'http.method': 'GET', + 'http.url': `${TEST_HOST}/test-req/${index}`, + url: `/test-req/${index}`, + 'server.address': 'sentry-test.io', + type: 'fetch', + }, + }), + ); +}); + +sentryTest('should attach `sentry-trace` header to fetch requests', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const requests = ( + await Promise.all([ + page.goto(url), + Promise.all([0, 1, 2].map(idx => page.waitForRequest(`${TEST_HOST}/test-req/${idx}`))), + ]) + )[1]; + + expect(requests).toHaveLength(3); + + const request1 = requests[0]; + const requestHeaders1 = request1.headers(); + expect(requestHeaders1).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + }); + + const request2 = requests[1]; + const requestHeaders2 = request2.headers(); + expect(requestHeaders2).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + 'x-test-header': 'existing-header', + }); + + const request3 = requests[2]; + const requestHeaders3 = request3.headers(); + expect(requestHeaders3).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/init.js b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/init.js new file mode 100644 index 000000000000..83076460599f --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/init.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [Sentry.browserTracingIntegration()], + tracesSampleRate: 1, +}); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/subject.js b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/subject.js new file mode 100644 index 000000000000..5fc9f91ab568 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/subject.js @@ -0,0 +1,12 @@ +const xhr_1 = new XMLHttpRequest(); +xhr_1.open('GET', '/test-req/0'); +xhr_1.send(); + +const xhr_2 = new XMLHttpRequest(); +xhr_2.open('GET', '/test-req/1'); +xhr_2.setRequestHeader('X-Test-Header', 'existing-header'); +xhr_2.send(); + +const xhr_3 = new XMLHttpRequest(); +xhr_3.open('GET', '/test-req/2'); +xhr_3.send(); diff --git a/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/test.ts b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/test.ts new file mode 100644 index 000000000000..f3dca9359b8b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/tracing/request/xhr-relative-url/test.ts @@ -0,0 +1,79 @@ +import { expect } from '@playwright/test'; + +import { TEST_HOST, sentryTest } from '../../../../utils/fixtures'; +import { + envelopeRequestParser, + shouldSkipTracingTest, + waitForTransactionRequestOnUrl, +} from '../../../../utils/helpers'; + +sentryTest('should create spans for xhr requests', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForTransactionRequestOnUrl(page, url); + const tracingEvent = envelopeRequestParser(req); + + const requestSpans = tracingEvent.spans?.filter(({ op }) => op === 'http.client'); + + expect(requestSpans).toHaveLength(3); + + requestSpans?.forEach((span, index) => + expect(span).toMatchObject({ + description: `GET /test-req/${index}`, + parent_span_id: tracingEvent.contexts?.trace?.span_id, + span_id: expect.any(String), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + trace_id: tracingEvent.contexts?.trace?.trace_id, + data: { + 'http.method': 'GET', + 'http.url': `${TEST_HOST}/test-req/${index}`, + url: `/test-req/${index}`, + 'server.address': 'sentry-test.io', + type: 'xhr', + }, + }), + ); +}); + +sentryTest('should attach `sentry-trace` header to xhr requests', async ({ getLocalTestUrl, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + const requests = ( + await Promise.all([ + page.goto(url), + Promise.all([0, 1, 2].map(idx => page.waitForRequest(`${TEST_HOST}/test-req/${idx}`))), + ]) + )[1]; + + expect(requests).toHaveLength(3); + + const request1 = requests[0]; + const requestHeaders1 = request1.headers(); + expect(requestHeaders1).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + }); + + const request2 = requests[1]; + const requestHeaders2 = request2.headers(); + expect(requestHeaders2).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + 'x-test-header': 'existing-header', + }); + + const request3 = requests[2]; + const requestHeaders3 = request3.headers(); + expect(requestHeaders3).toMatchObject({ + 'sentry-trace': expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/), + baggage: expect.any(String), + }); +}); diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index d99b2cf85536..98741cf40717 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -117,6 +117,18 @@ export function instrumentOutgoingRequests(_options?: Partial { const createdSpan = instrumentFetchRequest(handlerData, shouldCreateSpan, shouldAttachHeadersWithTargets, spans); + // We cannot use `window.location` in the generic fetch instrumentation, + // but we need it for reliable `server.address` attribute. + // so we extend this in here + if (createdSpan) { + const fullUrl = getFullURL(handlerData.fetchData.url); + const host = parseUrl(fullUrl).host; + createdSpan.setAttributes({ + 'http.url': fullUrl, + 'server.address': host, + }); + } + if (enableHTTPTimings && createdSpan) { addHTTPTimings(createdSpan); } @@ -312,7 +324,8 @@ export function xhrCallback( const hasParent = !!getActiveSpan(); - const parsedUrl = parseUrl(sentryXhrData.url); + const fullUrl = getFullURL(sentryXhrData.url); + const host = parseUrl(fullUrl).host; const span = shouldCreateSpanResult && hasParent @@ -321,9 +334,9 @@ export function xhrCallback( attributes: { type: 'xhr', 'http.method': sentryXhrData.method, - 'http.url': sentryXhrData.url, + 'http.url': fullUrl, url: sentryXhrData.url, - 'server.address': parsedUrl.host, + 'server.address': host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.browser', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client', }, @@ -387,3 +400,14 @@ function setHeaderOnXhr( // Error: InvalidStateError: Failed to execute 'setRequestHeader' on 'XMLHttpRequest': The object's state must be OPENED. } } + +function getFullURL(url: string): string { + try { + // By adding a base URL to new URL(), this will also work for relative urls + // If `url` is a full URL, the base URL is ignored anyhow + const parsed = new URL(url, WINDOW.location.origin); + return parsed.href; + } catch { + return url; + } +} diff --git a/packages/core/src/fetch.ts b/packages/core/src/fetch.ts index 8d102fac63a7..394f70d7fa47 100644 --- a/packages/core/src/fetch.ts +++ b/packages/core/src/fetch.ts @@ -54,22 +54,7 @@ export function instrumentFetchRequest( const span = spans[spanId]; if (span) { - if (handlerData.response) { - setHttpStatus(span, handlerData.response.status); - - const contentLength = - handlerData.response && handlerData.response.headers && handlerData.response.headers.get('content-length'); - - if (contentLength) { - const contentLengthNum = parseInt(contentLength); - if (contentLengthNum > 0) { - span.setAttribute('http.response_content_length', contentLengthNum); - } - } - } else if (handlerData.error) { - span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); - } - span.end(); + endSpan(span, handlerData); // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete spans[spanId]; @@ -84,7 +69,8 @@ export function instrumentFetchRequest( const hasParent = !!getActiveSpan(); - const parsedUrl = parseUrl(url); + const fullUrl = getFullURL(url); + const host = fullUrl ? parseUrl(fullUrl).host : undefined; const span = shouldCreateSpanResult && hasParent @@ -94,8 +80,8 @@ export function instrumentFetchRequest( url, type: 'fetch', 'http.method': method, - 'http.url': url, - 'server.address': parsedUrl.host, + 'http.url': fullUrl, + 'server.address': host, [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin, [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client', }, @@ -208,3 +194,31 @@ export function addTracingHeadersToFetchRequest( }; } } + +function getFullURL(url: string): string | undefined { + try { + const parsed = new URL(url); + return parsed.href; + } catch { + return undefined; + } +} + +function endSpan(span: Span, handlerData: HandlerDataFetch): void { + if (handlerData.response) { + setHttpStatus(span, handlerData.response.status); + + const contentLength = + handlerData.response && handlerData.response.headers && handlerData.response.headers.get('content-length'); + + if (contentLength) { + const contentLengthNum = parseInt(contentLength); + if (contentLengthNum > 0) { + span.setAttribute('http.response_content_length', contentLengthNum); + } + } + } else if (handlerData.error) { + span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' }); + } + span.end(); +} From 77effb70ee5388223866bfa9e81e4a30124ba5eb Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Tue, 16 Apr 2024 17:49:37 +0200 Subject: [PATCH 4/6] revert unneeded stuff --- .../{next.config.js.bak => next.config.js} | 0 packages/nextjs/test/integration/package.json | 2 +- .../nextjs/test/integration/package.json.bak | 41 ------------------- 3 files changed, 1 insertion(+), 42 deletions(-) rename packages/nextjs/test/integration/{next.config.js.bak => next.config.js} (100%) delete mode 100644 packages/nextjs/test/integration/package.json.bak diff --git a/packages/nextjs/test/integration/next.config.js.bak b/packages/nextjs/test/integration/next.config.js similarity index 100% rename from packages/nextjs/test/integration/next.config.js.bak rename to packages/nextjs/test/integration/next.config.js diff --git a/packages/nextjs/test/integration/package.json b/packages/nextjs/test/integration/package.json index a38da23b50f8..f4c547b5b687 100644 --- a/packages/nextjs/test/integration/package.json +++ b/packages/nextjs/test/integration/package.json @@ -12,7 +12,7 @@ }, "dependencies": { "@sentry/nextjs": "file:../../", - "next": "13.2.0", + "next": "latest", "react": "^18.2.0", "react-dom": "^18.2.0" }, diff --git a/packages/nextjs/test/integration/package.json.bak b/packages/nextjs/test/integration/package.json.bak deleted file mode 100644 index f4c547b5b687..000000000000 --- a/packages/nextjs/test/integration/package.json.bak +++ /dev/null @@ -1,41 +0,0 @@ -{ - "name": "with-typescript", - "license": "MIT", - "scripts": { - "dev": "next", - "build": "next build", - "predebug": "source ../integration_test_utils.sh && link_monorepo_packages '../../..' && yarn build", - "start": "next start", - "pretest": "yarn build", - "test:client": "playwright test", - "test:server": "jest --forceExit --runInBand" - }, - "dependencies": { - "@sentry/nextjs": "file:../../", - "next": "latest", - "react": "^18.2.0", - "react-dom": "^18.2.0" - }, - "devDependencies": { - "@types/node": "^15.3.1", - "@types/react": "17.0.47", - "@types/react-dom": "17.0.17", - "nock": "^13.1.0", - "typescript": "4.9.5", - "yargs": "^16.2.0" - }, - "resolutions": { - "@sentry/browser": "file:../../../browser", - "@sentry/core": "file:../../../core", - "@sentry/node": "file:../../../node", - "@sentry/opentelemetry": "file:../../../opentelemetry", - "@sentry/react": "file:../../../react", - "@sentry-internal/browser-utils": "file:../../../browser-utils", - "@sentry-internal/replay": "file:../../../replay-internal", - "@sentry-internal/replay-canvas": "file:../../../replay-canvas", - "@sentry-internal/feedback": "file:../../../feedback", - "@sentry/types": "file:../../../types", - "@sentry/utils": "file:../../../utils", - "@sentry/vercel-edge": "file:../../../vercel-edge" - } -} From c93409c10554f48d7070ba5671ccd6c5eab827f7 Mon Sep 17 00:00:00 2001 From: Francesco Novy Date: Wed, 17 Apr 2024 08:57:20 +0200 Subject: [PATCH 5/6] fix next test & better guard --- packages/browser/src/tracing/request.ts | 8 ++++---- .../test/integration/test/client/tracingFetch.test.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/browser/src/tracing/request.ts b/packages/browser/src/tracing/request.ts index 98741cf40717..45b373c2468e 100644 --- a/packages/browser/src/tracing/request.ts +++ b/packages/browser/src/tracing/request.ts @@ -122,7 +122,7 @@ export function instrumentOutgoingRequests(_options?: Partial Date: Wed, 17 Apr 2024 09:18:55 +0200 Subject: [PATCH 6/6] fix test & size limit --- .size-limit.js | 2 +- .../test-applications/nextjs-app-dir/tests/middleware.test.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.size-limit.js b/.size-limit.js index 6e005fd7c3e7..3b91cb51a3c5 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -208,7 +208,7 @@ module.exports = [ 'tls', ], gzip: true, - limit: '150 KB', + limit: '160 KB', }, ]; diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts index 79b07bd37a15..da0c4f303604 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts @@ -76,6 +76,8 @@ test('Should trace outgoing fetch requests inside middleware and create breadcru 'http.response.status_code': 200, type: 'fetch', url: 'http://localhost:3030/', + 'http.url': 'http://localhost:3030/', + 'server.address': 'localhost:3030', 'sentry.op': 'http.client', 'sentry.origin': 'auto.http.wintercg_fetch', },