diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ddfdfb5b45..23b13973847a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1103,7 +1103,7 @@ finished. This is useful for event emitters or similar. function middleware(_req, res, next) { return Sentry.startSpanManual({ name: 'middleware' }, (span, finish) => { res.once('finish', () => { - span?.setHttpStatus(res.status); + setHttpStatus(span, res.status); finish(); }); return next(); diff --git a/biome.json b/biome.json index a795c92a22df..ff5a6ac17286 100644 --- a/biome.json +++ b/biome.json @@ -47,6 +47,7 @@ "dev-packages/browser-integration-tests/suites/**/*.json", "dev-packages/browser-integration-tests/loader-suites/**/*.js", "dev-packages/browser-integration-tests/suites/stacktraces/**/*.js", + ".next/**/*", "**/fixtures/*/*.json", "**/*.min.js", ".next/**", diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index a66c942076b1..8a7cc3d90384 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -1,4 +1,4 @@ -import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; +import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, setHttpStatus } from '@sentry/core'; import { captureException, continueTrace, @@ -142,7 +142,7 @@ async function instrumentRequest( const originalResponse = await next(); if (span && originalResponse.status) { - span.setHttpStatus(originalResponse.status); + setHttpStatus(span, originalResponse.status); } const scope = getCurrentScope(); diff --git a/packages/bun/src/integrations/bunserver.ts b/packages/bun/src/integrations/bunserver.ts index aa8765638647..b1dc4c6892e0 100644 --- a/packages/bun/src/integrations/bunserver.ts +++ b/packages/bun/src/integrations/bunserver.ts @@ -7,6 +7,7 @@ import { convertIntegrationFnToClass, getCurrentScope, runWithAsyncContext, + setHttpStatus, startSpan, } from '@sentry/core'; import type { IntegrationFn } from '@sentry/types'; @@ -93,8 +94,9 @@ function instrumentBunServeOptions(serveOptions: Parameters[0] typeof serveOptions.fetch >); if (response && response.status) { - span?.setHttpStatus(response.status); - span?.setAttribute('http.response.status_code', response.status); + if (span) { + setHttpStatus(span, response.status); + } if (span instanceof Transaction) { const scope = getCurrentScope(); scope.setContext('response', { diff --git a/packages/core/src/tracing/errors.ts b/packages/core/src/tracing/errors.ts index 5a885dd1f090..229695afc58c 100644 --- a/packages/core/src/tracing/errors.ts +++ b/packages/core/src/tracing/errors.ts @@ -5,7 +5,7 @@ import { } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; -import type { SpanStatusType } from './span'; +import type { SpanStatusType } from './spanstatus'; import { getActiveTransaction } from './utils'; let errorsInstrumented = false; diff --git a/packages/core/src/tracing/index.ts b/packages/core/src/tracing/index.ts index ecdc5f595095..948196dd13c2 100644 --- a/packages/core/src/tracing/index.ts +++ b/packages/core/src/tracing/index.ts @@ -1,13 +1,14 @@ export { startIdleTransaction, addTracingExtensions } from './hubextensions'; export { IdleTransaction, TRACING_DEFAULTS } from './idletransaction'; export type { BeforeFinishCallback } from './idletransaction'; -export { Span, spanStatusfromHttpCode } from './span'; +export { Span } from './span'; export { Transaction } from './transaction'; // eslint-disable-next-line deprecation/deprecation export { extractTraceparentData, getActiveTransaction } from './utils'; // eslint-disable-next-line deprecation/deprecation export { SpanStatus } from './spanstatus'; -export type { SpanStatusType } from './span'; +export { setHttpStatus, spanStatusfromHttpCode } from './spanstatus'; +export type { SpanStatusType } from './spanstatus'; export { // eslint-disable-next-line deprecation/deprecation trace, diff --git a/packages/core/src/tracing/span.ts b/packages/core/src/tracing/span.ts index a52d6bd5e9c8..165677455d7f 100644 --- a/packages/core/src/tracing/span.ts +++ b/packages/core/src/tracing/span.ts @@ -26,6 +26,8 @@ import { spanToTraceContext, spanToTraceHeader, } from '../utils/spanUtils'; +import type { SpanStatusType } from './spanstatus'; +import { setHttpStatus } from './spanstatus'; /** * Keeps track of finished spans for a given transaction @@ -470,16 +472,10 @@ export class Span implements SpanInterface { /** * @inheritDoc + * @deprecated Use top-level `setHttpStatus()` instead. */ public setHttpStatus(httpStatus: number): this { - // eslint-disable-next-line deprecation/deprecation - this.setTag('http.status_code', String(httpStatus)); - // eslint-disable-next-line deprecation/deprecation - this.setData('http.response.status_code', httpStatus); - const spanStatus = spanStatusfromHttpCode(httpStatus); - if (spanStatus !== 'unknown_error') { - this.setStatus(spanStatus); - } + setHttpStatus(this, httpStatus); return this; } @@ -675,85 +671,3 @@ export class Span implements SpanInterface { return hasData ? data : attributes; } } - -export type SpanStatusType = - /** The operation completed successfully. */ - | 'ok' - /** Deadline expired before operation could complete. */ - | 'deadline_exceeded' - /** 401 Unauthorized (actually does mean unauthenticated according to RFC 7235) */ - | 'unauthenticated' - /** 403 Forbidden */ - | 'permission_denied' - /** 404 Not Found. Some requested entity (file or directory) was not found. */ - | 'not_found' - /** 429 Too Many Requests */ - | 'resource_exhausted' - /** Client specified an invalid argument. 4xx. */ - | 'invalid_argument' - /** 501 Not Implemented */ - | 'unimplemented' - /** 503 Service Unavailable */ - | 'unavailable' - /** Other/generic 5xx. */ - | 'internal_error' - /** Unknown. Any non-standard HTTP status code. */ - | 'unknown_error' - /** The operation was cancelled (typically by the user). */ - | 'cancelled' - /** Already exists (409) */ - | 'already_exists' - /** Operation was rejected because the system is not in a state required for the operation's */ - | 'failed_precondition' - /** The operation was aborted, typically due to a concurrency issue. */ - | 'aborted' - /** Operation was attempted past the valid range. */ - | 'out_of_range' - /** Unrecoverable data loss or corruption */ - | 'data_loss'; - -/** - * Converts a HTTP status code into a {@link SpanStatusType}. - * - * @param httpStatus The HTTP response status code. - * @returns The span status or unknown_error. - */ -export function spanStatusfromHttpCode(httpStatus: number): SpanStatusType { - if (httpStatus < 400 && httpStatus >= 100) { - return 'ok'; - } - - if (httpStatus >= 400 && httpStatus < 500) { - switch (httpStatus) { - case 401: - return 'unauthenticated'; - case 403: - return 'permission_denied'; - case 404: - return 'not_found'; - case 409: - return 'already_exists'; - case 413: - return 'failed_precondition'; - case 429: - return 'resource_exhausted'; - default: - return 'invalid_argument'; - } - } - - if (httpStatus >= 500 && httpStatus < 600) { - switch (httpStatus) { - case 501: - return 'unimplemented'; - case 503: - return 'unavailable'; - case 504: - return 'deadline_exceeded'; - default: - return 'internal_error'; - } - } - - return 'unknown_error'; -} diff --git a/packages/core/src/tracing/spanstatus.ts b/packages/core/src/tracing/spanstatus.ts index 6a758d95ee84..f38c397f1ae0 100644 --- a/packages/core/src/tracing/spanstatus.ts +++ b/packages/core/src/tracing/spanstatus.ts @@ -1,3 +1,5 @@ +import type { Span } from '@sentry/types'; + /** The status of an Span. * * @deprecated Use string literals - if you require type casting, cast to SpanStatusType type @@ -38,3 +40,108 @@ export enum SpanStatus { /** Unrecoverable data loss or corruption */ DataLoss = 'data_loss', } + +export type SpanStatusType = + /** The operation completed successfully. */ + | 'ok' + /** Deadline expired before operation could complete. */ + | 'deadline_exceeded' + /** 401 Unauthorized (actually does mean unauthenticated according to RFC 7235) */ + | 'unauthenticated' + /** 403 Forbidden */ + | 'permission_denied' + /** 404 Not Found. Some requested entity (file or directory) was not found. */ + | 'not_found' + /** 429 Too Many Requests */ + | 'resource_exhausted' + /** Client specified an invalid argument. 4xx. */ + | 'invalid_argument' + /** 501 Not Implemented */ + | 'unimplemented' + /** 503 Service Unavailable */ + | 'unavailable' + /** Other/generic 5xx. */ + | 'internal_error' + /** Unknown. Any non-standard HTTP status code. */ + | 'unknown_error' + /** The operation was cancelled (typically by the user). */ + | 'cancelled' + /** Already exists (409) */ + | 'already_exists' + /** Operation was rejected because the system is not in a state required for the operation's */ + | 'failed_precondition' + /** The operation was aborted, typically due to a concurrency issue. */ + | 'aborted' + /** Operation was attempted past the valid range. */ + | 'out_of_range' + /** Unrecoverable data loss or corruption */ + | 'data_loss'; + +/** + * Converts a HTTP status code into a {@link SpanStatusType}. + * + * @param httpStatus The HTTP response status code. + * @returns The span status or unknown_error. + */ +export function spanStatusfromHttpCode(httpStatus: number): SpanStatusType { + if (httpStatus < 400 && httpStatus >= 100) { + return 'ok'; + } + + if (httpStatus >= 400 && httpStatus < 500) { + switch (httpStatus) { + case 401: + return 'unauthenticated'; + case 403: + return 'permission_denied'; + case 404: + return 'not_found'; + case 409: + return 'already_exists'; + case 413: + return 'failed_precondition'; + case 429: + return 'resource_exhausted'; + default: + return 'invalid_argument'; + } + } + + if (httpStatus >= 500 && httpStatus < 600) { + switch (httpStatus) { + case 501: + return 'unimplemented'; + case 503: + return 'unavailable'; + case 504: + return 'deadline_exceeded'; + default: + return 'internal_error'; + } + } + + return 'unknown_error'; +} + +/** + * Sets the Http status attributes on the current span based on the http code. + * Additionally, the span's status is updated, depending on the http code. + */ +export function setHttpStatus(span: Span, httpStatus: number): void { + // TODO (v8): Remove these calls + // Relay does not require us to send the status code as a tag + // For now, just because users might expect it to land as a tag we keep sending it. + // Same with data. + // In v8, we replace both, simply with + // span.setAttribute('http.response.status_code', httpStatus); + + // eslint-disable-next-line deprecation/deprecation + span.setTag('http.status_code', String(httpStatus)); + // eslint-disable-next-line deprecation/deprecation + span.setData('http.response.status_code', httpStatus); + + const spanStatus = spanStatusfromHttpCode(httpStatus); + if (spanStatus !== 'unknown_error') { + span.setStatus(spanStatus); + } +} diff --git a/packages/core/test/lib/tracing/spanstatus.test.ts b/packages/core/test/lib/tracing/spanstatus.test.ts new file mode 100644 index 000000000000..559aa101c642 --- /dev/null +++ b/packages/core/test/lib/tracing/spanstatus.test.ts @@ -0,0 +1,41 @@ +import { Span, setHttpStatus, spanToJSON } from '../../../src/index'; + +describe('setHttpStatus', () => { + it.each([ + [200, 'ok'], + [300, 'ok'], + [401, 'unauthenticated'], + [403, 'permission_denied'], + [404, 'not_found'], + [409, 'already_exists'], + [413, 'failed_precondition'], + [429, 'resource_exhausted'], + [455, 'invalid_argument'], + [501, 'unimplemented'], + [503, 'unavailable'], + [504, 'deadline_exceeded'], + [520, 'internal_error'], + ])('applies the correct span status and http status code to the span (%s - $%s)', (code, status) => { + const span = new Span({ name: 'test' }); + + setHttpStatus(span!, code); + + const { status: spanStatus, data, tags } = spanToJSON(span!); + + expect(spanStatus).toBe(status); + expect(data).toMatchObject({ 'http.response.status_code': code }); + expect(tags).toMatchObject({ 'http.status_code': String(code) }); + }); + + it("doesn't set the status for an unknown http status code", () => { + const span = new Span({ name: 'test' }); + + setHttpStatus(span!, 600); + + const { status: spanStatus, data, tags } = spanToJSON(span!); + + expect(spanStatus).toBeUndefined(); + expect(data).toMatchObject({ 'http.response.status_code': 600 }); + expect(tags).toMatchObject({ 'http.status_code': '600' }); + }); +}); diff --git a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts index 109a586d7cd6..fd98fe2328ee 100644 --- a/packages/nextjs/src/common/utils/edgeWrapperUtils.ts +++ b/packages/nextjs/src/common/utils/edgeWrapperUtils.ts @@ -5,6 +5,7 @@ import { captureException, continueTrace, handleCallbackErrors, + setHttpStatus, startSpan, } from '@sentry/core'; import { winterCGRequestToRequestData } from '@sentry/utils'; @@ -67,10 +68,12 @@ export function withEdgeWrapping( }, ); - if (handlerResult instanceof Response) { - span?.setHttpStatus(handlerResult.status); - } else { - span?.setStatus('ok'); + if (span) { + if (handlerResult instanceof Response) { + setHttpStatus(span, handlerResult.status); + } else { + span.setStatus('ok'); + } } return handlerResult; diff --git a/packages/nextjs/src/common/utils/responseEnd.ts b/packages/nextjs/src/common/utils/responseEnd.ts index e59a99fb0ebb..c12d19f1c6fa 100644 --- a/packages/nextjs/src/common/utils/responseEnd.ts +++ b/packages/nextjs/src/common/utils/responseEnd.ts @@ -1,5 +1,5 @@ import type { ServerResponse } from 'http'; -import { flush } from '@sentry/core'; +import { flush, setHttpStatus } from '@sentry/core'; import type { Transaction } from '@sentry/types'; import { fill, logger } from '@sentry/utils'; @@ -41,7 +41,7 @@ export function autoEndTransactionOnResponseEnd(transaction: Transaction, res: S /** Finish the given response's transaction and set HTTP status data */ export function finishTransaction(transaction: Transaction | undefined, res: ServerResponse): void { if (transaction) { - transaction.setHttpStatus(res.statusCode); + setHttpStatus(transaction, res.statusCode); transaction.end(); } } diff --git a/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts index 91c95e264875..61a08ba18891 100644 --- a/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapApiHandlerWithSentry.ts @@ -5,6 +5,7 @@ import { continueTrace, getCurrentScope, runWithAsyncContext, + setHttpStatus, startSpanManual, } from '@sentry/core'; import { consoleSandbox, isString, logger, objectify, stripUrlQueryAndFragment } from '@sentry/utils'; @@ -123,8 +124,10 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri // eslint-disable-next-line @typescript-eslint/unbound-method res.end = new Proxy(res.end, { apply(target, thisArg, argArray) { - span?.setHttpStatus(res.statusCode); - span?.end(); + if (span) { + setHttpStatus(span, res.statusCode); + span.end(); + } if (platformSupportsStreaming() && !wrappingTarget.__sentry_test_doesnt_support_streaming__) { target.apply(thisArg, argArray); } else { @@ -180,8 +183,10 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri res.statusCode = 500; res.statusMessage = 'Internal Server Error'; - span?.setHttpStatus(res.statusCode); - span?.end(); + if (span) { + setHttpStatus(span, res.statusCode); + span.end(); + } // Make sure we have a chance to finish the transaction and flush events to Sentry before the handler errors // out. (Apps which are deployed on Vercel run their API routes in lambdas, and those lambdas will shut down the diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index a1067b1577e4..99b9cf99b9b9 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -4,6 +4,7 @@ import { getCurrentScope, handleCallbackErrors, runWithAsyncContext, + setHttpStatus, startSpan, } from '@sentry/core'; import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'; @@ -66,7 +67,7 @@ export function wrapRouteHandlerWithSentry any>( ); try { - span?.setHttpStatus(response.status); + span && setHttpStatus(span, response.status); } catch { // best effort } diff --git a/packages/node/src/handlers.ts b/packages/node/src/handlers.ts index 892aabd2dd84..b1140d0d9c28 100644 --- a/packages/node/src/handlers.ts +++ b/packages/node/src/handlers.ts @@ -10,6 +10,7 @@ import { getCurrentScope, hasTracingEnabled, runWithAsyncContext, + setHttpStatus, startTransaction, withScope, } from '@sentry/core'; @@ -105,7 +106,7 @@ export function tracingHandler(): ( // closes setImmediate(() => { addRequestDataToTransaction(transaction, req); - transaction.setHttpStatus(res.statusCode); + setHttpStatus(transaction, res.statusCode); transaction.end(); }); }); diff --git a/packages/node/src/integrations/hapi/index.ts b/packages/node/src/integrations/hapi/index.ts index 42335f7c4ce5..c80265926ce4 100644 --- a/packages/node/src/integrations/hapi/index.ts +++ b/packages/node/src/integrations/hapi/index.ts @@ -6,6 +6,7 @@ import { getActiveTransaction, getCurrentScope, getDynamicSamplingContextFromSpan, + setHttpStatus, spanToTraceHeader, startTransaction, } from '@sentry/core'; @@ -117,11 +118,11 @@ export const hapiTracingPlugin = { // eslint-disable-next-line deprecation/deprecation const transaction = getActiveTransaction(); - if (request.response && isResponseObject(request.response) && transaction) { - transaction.setHttpStatus(request.response.statusCode); - } - if (transaction) { + if (request.response && isResponseObject(request.response)) { + setHttpStatus(transaction, request.response.statusCode); + } + transaction.end(); } diff --git a/packages/node/src/integrations/http.ts b/packages/node/src/integrations/http.ts index e3f6d164d991..47cd73304e06 100644 --- a/packages/node/src/integrations/http.ts +++ b/packages/node/src/integrations/http.ts @@ -10,6 +10,7 @@ import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, isSentryRequestUrl, + setHttpStatus, spanToJSON, spanToTraceHeader, } from '@sentry/core'; @@ -302,7 +303,7 @@ function _createWrappedRequestMethodFactory( } if (requestSpan) { if (res.statusCode) { - requestSpan.setHttpStatus(res.statusCode); + setHttpStatus(requestSpan, res.statusCode); } requestSpan.updateName( cleanSpanDescription(spanToJSON(requestSpan).description || '', requestOptions, req) || '', @@ -318,7 +319,7 @@ function _createWrappedRequestMethodFactory( addRequestBreadcrumb('error', data, req); } if (requestSpan) { - requestSpan.setHttpStatus(500); + setHttpStatus(requestSpan, 500); requestSpan.updateName( cleanSpanDescription(spanToJSON(requestSpan).description || '', requestOptions, req) || '', ); diff --git a/packages/node/src/integrations/undici/index.ts b/packages/node/src/integrations/undici/index.ts index ef875891b3ef..33f1472420f8 100644 --- a/packages/node/src/integrations/undici/index.ts +++ b/packages/node/src/integrations/undici/index.ts @@ -6,6 +6,7 @@ import { getDynamicSamplingContextFromClient, getDynamicSamplingContextFromSpan, isSentryRequestUrl, + setHttpStatus, spanToTraceHeader, } from '@sentry/core'; import type { EventProcessor, Integration, Span } from '@sentry/types'; @@ -211,7 +212,7 @@ export class Undici implements Integration { const span = request.__sentry_span__; if (span) { - span.setHttpStatus(response.statusCode); + setHttpStatus(span, response.statusCode); span.end(); } diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index f90e9dfabd77..1ed13e9f28ec 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -8,6 +8,7 @@ import { getDynamicSamplingContextFromSpan, hasTracingEnabled, runWithAsyncContext, + setHttpStatus, spanToJSON, spanToTraceHeader, } from '@sentry/core'; @@ -495,7 +496,7 @@ function wrapRequestHandler(origRequestHandler: RequestHandler, build: ServerBui const res = (await origRequestHandler.call(this, request, loadContext)) as Response; if (isResponse(res)) { - transaction.setHttpStatus(res.status); + setHttpStatus(transaction, res.status); } transaction.end(); diff --git a/packages/remix/src/utils/serverAdapters/express.ts b/packages/remix/src/utils/serverAdapters/express.ts index d3acd7633132..8f05de780381 100644 --- a/packages/remix/src/utils/serverAdapters/express.ts +++ b/packages/remix/src/utils/serverAdapters/express.ts @@ -1,4 +1,11 @@ -import { getClient, getCurrentHub, getCurrentScope, hasTracingEnabled, runWithAsyncContext } from '@sentry/core'; +import { + getClient, + getCurrentHub, + getCurrentScope, + hasTracingEnabled, + runWithAsyncContext, + setHttpStatus, +} from '@sentry/core'; import { flush } from '@sentry/node'; import type { Transaction } from '@sentry/types'; import { extractRequestData, fill, isString, logger } from '@sentry/utils'; @@ -151,7 +158,7 @@ async function finishSentryProcessing(res: AugmentedExpressResponse): Promise void), encoding?: string | (() => void), cb?: () => void): any { - span?.setHttpStatus(res.statusCode); - span?.end(); + if (span) { + setHttpStatus(span, res.statusCode); + span.end(); + } // eslint-disable-next-line @typescript-eslint/no-floating-promises flush(options.flushTimeout) diff --git a/packages/serverless/test/gcpfunction.test.ts b/packages/serverless/test/gcpfunction.test.ts index 8fb51d3bf368..b9794553961f 100644 --- a/packages/serverless/test/gcpfunction.test.ts +++ b/packages/serverless/test/gcpfunction.test.ts @@ -1,5 +1,7 @@ import * as domain from 'domain'; +import * as SentryCore from '@sentry/core'; import * as SentryNode from '@sentry/node'; + import type { Event, Integration } from '@sentry/types'; import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core'; @@ -16,6 +18,8 @@ import type { } from '../src/gcpfunction/general'; describe('GCPFunction', () => { + const setHttpStatusSpy = jest.spyOn(SentryCore, 'setHttpStatus').mockImplementation(() => {}); + afterEach(() => { // @ts-expect-error see "Why @ts-expect-error" note SentryNode.resetMocks(); @@ -114,7 +118,7 @@ describe('GCPFunction', () => { expect(SentryNode.startSpanManual).toBeCalledWith(fakeTransactionContext, expect.any(Function)); // @ts-expect-error see "Why @ts-expect-error" note - expect(SentryNode.fakeSpan.setHttpStatus).toBeCalledWith(200); + expect(setHttpStatusSpy).toBeCalledWith(SentryNode.fakeSpan, 200); // @ts-expect-error see "Why @ts-expect-error" note expect(SentryNode.fakeSpan.end).toBeCalled(); expect(SentryNode.flush).toBeCalledWith(2000); diff --git a/packages/sveltekit/src/server/handle.ts b/packages/sveltekit/src/server/handle.ts index 5572e64060d3..42f95c905ca5 100644 --- a/packages/sveltekit/src/server/handle.ts +++ b/packages/sveltekit/src/server/handle.ts @@ -3,6 +3,7 @@ import { getActiveSpan, getCurrentScope, getDynamicSamplingContextFromSpan, + setHttpStatus, spanToTraceHeader, } from '@sentry/core'; import { getActiveTransaction, runWithAsyncContext, startSpan } from '@sentry/core'; @@ -191,7 +192,7 @@ async function instrumentHandle( transformPageChunk: addSentryCodeToPage(options), }); if (span) { - span.setHttpStatus(res.status); + setHttpStatus(span, res.status); } return res; }, diff --git a/packages/tracing-internal/src/browser/request.ts b/packages/tracing-internal/src/browser/request.ts index d2600f02629e..56ba74f7b97f 100644 --- a/packages/tracing-internal/src/browser/request.ts +++ b/packages/tracing-internal/src/browser/request.ts @@ -7,6 +7,7 @@ import { getDynamicSamplingContextFromSpan, getRootSpan, hasTracingEnabled, + setHttpStatus, spanToJSON, spanToTraceHeader, startInactiveSpan, @@ -266,7 +267,7 @@ export function xhrCallback( const span = spans[spanId]; if (span && sentryXhrData.status_code !== undefined) { - span.setHttpStatus(sentryXhrData.status_code); + setHttpStatus(span, sentryXhrData.status_code); span.end(); // eslint-disable-next-line @typescript-eslint/no-dynamic-delete diff --git a/packages/tracing-internal/src/common/fetch.ts b/packages/tracing-internal/src/common/fetch.ts index 8f9da76488ad..2703289b4d0c 100644 --- a/packages/tracing-internal/src/common/fetch.ts +++ b/packages/tracing-internal/src/common/fetch.ts @@ -6,6 +6,7 @@ import { getDynamicSamplingContextFromSpan, getRootSpan, hasTracingEnabled, + setHttpStatus, spanToTraceHeader, startInactiveSpan, } from '@sentry/core'; @@ -53,7 +54,7 @@ export function instrumentFetchRequest( const span = spans[spanId]; if (span) { if (handlerData.response) { - span.setHttpStatus(handlerData.response.status); + setHttpStatus(span, handlerData.response.status); const contentLength = handlerData.response && handlerData.response.headers && handlerData.response.headers.get('content-length'); diff --git a/packages/tracing/test/span.test.ts b/packages/tracing/test/span.test.ts index 4a7e1537f394..ae13f42ea698 100644 --- a/packages/tracing/test/span.test.ts +++ b/packages/tracing/test/span.test.ts @@ -98,6 +98,7 @@ describe('Span', () => { expect((span.getTraceContext() as any).status).toBe('permission_denied'); }); + // TODO (v8): Remove test('setHttpStatus', () => { const span = new Span({}); span.setHttpStatus(404); diff --git a/packages/types/src/span.ts b/packages/types/src/span.ts index e34940b35d65..9a0cfc25725f 100644 --- a/packages/types/src/span.ts +++ b/packages/types/src/span.ts @@ -326,6 +326,7 @@ export interface Span extends Omit { /** * Sets the status attribute on the current span based on the http code * @param httpStatus http code used to set the status + * @deprecated Use top-level `setHttpStatus()` instead. */ setHttpStatus(httpStatus: number): this;