Skip to content

Commit 414f7c9

Browse files
committed
wip
1 parent 1cceeae commit 414f7c9

File tree

24 files changed

+182
-127
lines changed

24 files changed

+182
-127
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ finished. This is useful for event emitters or similar.
10171017
function middleware(_req, res, next) {
10181018
return Sentry.startSpanManual({ name: 'middleware' }, (span, finish) => {
10191019
res.once('finish', () => {
1020-
span?.setHttpStatus(res.status);
1020+
setHttpStatus(span, res.status);
10211021
finish();
10221022
});
10231023
return next();

biome.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"dev-packages/browser-integration-tests/suites/**/*.json",
4848
"dev-packages/browser-integration-tests/loader-suites/**/*.js",
4949
"dev-packages/browser-integration-tests/suites/stacktraces/**/*.js",
50+
".next/**/*",
5051
"**/fixtures/*/*.json",
5152
"**/*.min.js"
5253
]

packages/astro/src/server/middleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
1+
import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, setHttpStatus } from '@sentry/core';
22
import {
33
captureException,
44
continueTrace,
@@ -140,7 +140,7 @@ async function instrumentRequest(
140140
const originalResponse = await next();
141141

142142
if (span && originalResponse.status) {
143-
span.setHttpStatus(originalResponse.status);
143+
setHttpStatus(span, originalResponse.status);
144144
}
145145

146146
const scope = getCurrentScope();

packages/bun/src/integrations/bunserver.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
convertIntegrationFnToClass,
77
getCurrentScope,
88
runWithAsyncContext,
9+
setHttpStatus,
910
startSpan,
1011
} from '@sentry/core';
1112
import type { IntegrationFn } from '@sentry/types';
@@ -90,8 +91,9 @@ function instrumentBunServeOptions(serveOptions: Parameters<typeof Bun.serve>[0]
9091
typeof serveOptions.fetch
9192
>);
9293
if (response && response.status) {
93-
span?.setHttpStatus(response.status);
94-
span?.setAttribute('http.response.status_code', response.status);
94+
if (span) {
95+
setHttpStatus(span, response.status);
96+
}
9597
if (span instanceof Transaction) {
9698
const scope = getCurrentScope();
9799
scope.setContext('response', {

packages/core/src/tracing/errors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from '@sentry/utils';
66

77
import { DEBUG_BUILD } from '../debug-build';
8-
import type { SpanStatusType } from './span';
8+
import type { SpanStatusType } from './spanstatus';
99
import { getActiveTransaction } from './utils';
1010

1111
let errorsInstrumented = false;

packages/core/src/tracing/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
export { startIdleTransaction, addTracingExtensions } from './hubextensions';
22
export { IdleTransaction, TRACING_DEFAULTS } from './idletransaction';
33
export type { BeforeFinishCallback } from './idletransaction';
4-
export { Span, spanStatusfromHttpCode } from './span';
4+
export { Span } from './span';
55
export { Transaction } from './transaction';
66
// eslint-disable-next-line deprecation/deprecation
77
export { extractTraceparentData, getActiveTransaction } from './utils';
88
// eslint-disable-next-line deprecation/deprecation
99
export { SpanStatus } from './spanstatus';
10-
export type { SpanStatusType } from './span';
10+
export { setHttpStatus, spanStatusfromHttpCode } from './spanstatus';
11+
export type { SpanStatusType } from './spanstatus';
1112
export {
1213
// eslint-disable-next-line deprecation/deprecation
1314
trace,

packages/core/src/tracing/span.ts

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {
2626
spanToTraceContext,
2727
spanToTraceHeader,
2828
} from '../utils/spanUtils';
29+
import type { SpanStatusType } from './spanstatus';
30+
import { setHttpStatus } from './spanstatus';
2931

3032
/**
3133
* Keeps track of finished spans for a given transaction
@@ -469,16 +471,10 @@ export class Span implements SpanInterface {
469471

470472
/**
471473
* @inheritDoc
474+
* @deprecated Use top-level `setHttpStatus()` instead.
472475
*/
473476
public setHttpStatus(httpStatus: number): this {
474-
// eslint-disable-next-line deprecation/deprecation
475-
this.setTag('http.status_code', String(httpStatus));
476-
// eslint-disable-next-line deprecation/deprecation
477-
this.setData('http.response.status_code', httpStatus);
478-
const spanStatus = spanStatusfromHttpCode(httpStatus);
479-
if (spanStatus !== 'unknown_error') {
480-
this.setStatus(spanStatus);
481-
}
477+
setHttpStatus(this, httpStatus);
482478
return this;
483479
}
484480

@@ -674,85 +670,3 @@ export class Span implements SpanInterface {
674670
return hasData ? data : attributes;
675671
}
676672
}
677-
678-
export type SpanStatusType =
679-
/** The operation completed successfully. */
680-
| 'ok'
681-
/** Deadline expired before operation could complete. */
682-
| 'deadline_exceeded'
683-
/** 401 Unauthorized (actually does mean unauthenticated according to RFC 7235) */
684-
| 'unauthenticated'
685-
/** 403 Forbidden */
686-
| 'permission_denied'
687-
/** 404 Not Found. Some requested entity (file or directory) was not found. */
688-
| 'not_found'
689-
/** 429 Too Many Requests */
690-
| 'resource_exhausted'
691-
/** Client specified an invalid argument. 4xx. */
692-
| 'invalid_argument'
693-
/** 501 Not Implemented */
694-
| 'unimplemented'
695-
/** 503 Service Unavailable */
696-
| 'unavailable'
697-
/** Other/generic 5xx. */
698-
| 'internal_error'
699-
/** Unknown. Any non-standard HTTP status code. */
700-
| 'unknown_error'
701-
/** The operation was cancelled (typically by the user). */
702-
| 'cancelled'
703-
/** Already exists (409) */
704-
| 'already_exists'
705-
/** Operation was rejected because the system is not in a state required for the operation's */
706-
| 'failed_precondition'
707-
/** The operation was aborted, typically due to a concurrency issue. */
708-
| 'aborted'
709-
/** Operation was attempted past the valid range. */
710-
| 'out_of_range'
711-
/** Unrecoverable data loss or corruption */
712-
| 'data_loss';
713-
714-
/**
715-
* Converts a HTTP status code into a {@link SpanStatusType}.
716-
*
717-
* @param httpStatus The HTTP response status code.
718-
* @returns The span status or unknown_error.
719-
*/
720-
export function spanStatusfromHttpCode(httpStatus: number): SpanStatusType {
721-
if (httpStatus < 400 && httpStatus >= 100) {
722-
return 'ok';
723-
}
724-
725-
if (httpStatus >= 400 && httpStatus < 500) {
726-
switch (httpStatus) {
727-
case 401:
728-
return 'unauthenticated';
729-
case 403:
730-
return 'permission_denied';
731-
case 404:
732-
return 'not_found';
733-
case 409:
734-
return 'already_exists';
735-
case 413:
736-
return 'failed_precondition';
737-
case 429:
738-
return 'resource_exhausted';
739-
default:
740-
return 'invalid_argument';
741-
}
742-
}
743-
744-
if (httpStatus >= 500 && httpStatus < 600) {
745-
switch (httpStatus) {
746-
case 501:
747-
return 'unimplemented';
748-
case 503:
749-
return 'unavailable';
750-
case 504:
751-
return 'deadline_exceeded';
752-
default:
753-
return 'internal_error';
754-
}
755-
}
756-
757-
return 'unknown_error';
758-
}

packages/core/src/tracing/spanstatus.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Span } from '@sentry/types';
2+
13
/** The status of an Span.
24
*
35
* @deprecated Use string literals - if you require type casting, cast to SpanStatusType type
@@ -38,3 +40,105 @@ export enum SpanStatus {
3840
/** Unrecoverable data loss or corruption */
3941
DataLoss = 'data_loss',
4042
}
43+
44+
export type SpanStatusType =
45+
/** The operation completed successfully. */
46+
| 'ok'
47+
/** Deadline expired before operation could complete. */
48+
| 'deadline_exceeded'
49+
/** 401 Unauthorized (actually does mean unauthenticated according to RFC 7235) */
50+
| 'unauthenticated'
51+
/** 403 Forbidden */
52+
| 'permission_denied'
53+
/** 404 Not Found. Some requested entity (file or directory) was not found. */
54+
| 'not_found'
55+
/** 429 Too Many Requests */
56+
| 'resource_exhausted'
57+
/** Client specified an invalid argument. 4xx. */
58+
| 'invalid_argument'
59+
/** 501 Not Implemented */
60+
| 'unimplemented'
61+
/** 503 Service Unavailable */
62+
| 'unavailable'
63+
/** Other/generic 5xx. */
64+
| 'internal_error'
65+
/** Unknown. Any non-standard HTTP status code. */
66+
| 'unknown_error'
67+
/** The operation was cancelled (typically by the user). */
68+
| 'cancelled'
69+
/** Already exists (409) */
70+
| 'already_exists'
71+
/** Operation was rejected because the system is not in a state required for the operation's */
72+
| 'failed_precondition'
73+
/** The operation was aborted, typically due to a concurrency issue. */
74+
| 'aborted'
75+
/** Operation was attempted past the valid range. */
76+
| 'out_of_range'
77+
/** Unrecoverable data loss or corruption */
78+
| 'data_loss';
79+
80+
/**
81+
* Converts a HTTP status code into a {@link SpanStatusType}.
82+
*
83+
* @param httpStatus The HTTP response status code.
84+
* @returns The span status or unknown_error.
85+
*/
86+
export function spanStatusfromHttpCode(httpStatus: number): SpanStatusType {
87+
if (httpStatus < 400 && httpStatus >= 100) {
88+
return 'ok';
89+
}
90+
91+
if (httpStatus >= 400 && httpStatus < 500) {
92+
switch (httpStatus) {
93+
case 401:
94+
return 'unauthenticated';
95+
case 403:
96+
return 'permission_denied';
97+
case 404:
98+
return 'not_found';
99+
case 409:
100+
return 'already_exists';
101+
case 413:
102+
return 'failed_precondition';
103+
case 429:
104+
return 'resource_exhausted';
105+
default:
106+
return 'invalid_argument';
107+
}
108+
}
109+
110+
if (httpStatus >= 500 && httpStatus < 600) {
111+
switch (httpStatus) {
112+
case 501:
113+
return 'unimplemented';
114+
case 503:
115+
return 'unavailable';
116+
case 504:
117+
return 'deadline_exceeded';
118+
default:
119+
return 'internal_error';
120+
}
121+
}
122+
123+
return 'unknown_error';
124+
}
125+
126+
/**
127+
* Sets the Http status attributes on the current span based on the http code.
128+
* Additionally, the span's status is updated, depending on the http code.
129+
*/
130+
export function setHttpStatus(span: Span, httpStatus: number): void {
131+
span.setAttribute('http.status_code', String(httpStatus));
132+
span.setAttribute('http.response.status_code', httpStatus);
133+
134+
// TODO (v8): Remove these calls
135+
// eslint-disable-next-line deprecation/deprecation
136+
span.setTag('http.status_code', String(httpStatus));
137+
// eslint-disable-next-line deprecation/deprecation
138+
span.setTag('http.response.status_code', httpStatus);
139+
140+
const spanStatus = spanStatusfromHttpCode(httpStatus);
141+
if (spanStatus !== 'unknown_error') {
142+
span.setStatus(spanStatus);
143+
}
144+
}

packages/nextjs/src/common/utils/edgeWrapperUtils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
captureException,
55
continueTrace,
66
handleCallbackErrors,
7+
setHttpStatus,
78
startSpan,
89
} from '@sentry/core';
910
import { winterCGRequestToRequestData } from '@sentry/utils';
@@ -64,10 +65,12 @@ export function withEdgeWrapping<H extends EdgeRouteHandler>(
6465
},
6566
);
6667

67-
if (handlerResult instanceof Response) {
68-
span?.setHttpStatus(handlerResult.status);
69-
} else {
70-
span?.setStatus('ok');
68+
if (span) {
69+
if (handlerResult instanceof Response) {
70+
setHttpStatus(span, handlerResult.status);
71+
} else {
72+
span.setStatus('ok');
73+
}
7174
}
7275

7376
return handlerResult;

packages/nextjs/src/common/utils/responseEnd.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ServerResponse } from 'http';
2-
import { flush } from '@sentry/core';
2+
import { flush, setHttpStatus } from '@sentry/core';
33
import type { Transaction } from '@sentry/types';
44
import { fill, logger } from '@sentry/utils';
55

@@ -41,7 +41,7 @@ export function autoEndTransactionOnResponseEnd(transaction: Transaction, res: S
4141
/** Finish the given response's transaction and set HTTP status data */
4242
export function finishTransaction(transaction: Transaction | undefined, res: ServerResponse): void {
4343
if (transaction) {
44-
transaction.setHttpStatus(res.statusCode);
44+
setHttpStatus(transaction, res.statusCode);
4545
transaction.end();
4646
}
4747
}

packages/nextjs/src/common/wrapApiHandlerWithSentry.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
continueTrace,
66
getCurrentScope,
77
runWithAsyncContext,
8+
setHttpStatus,
89
startSpanManual,
910
} from '@sentry/core';
1011
import { consoleSandbox, isString, logger, objectify, stripUrlQueryAndFragment } from '@sentry/utils';
@@ -122,8 +123,10 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri
122123
// eslint-disable-next-line @typescript-eslint/unbound-method
123124
res.end = new Proxy(res.end, {
124125
apply(target, thisArg, argArray) {
125-
span?.setHttpStatus(res.statusCode);
126-
span?.end();
126+
if (span) {
127+
setHttpStatus(span, res.statusCode);
128+
span.end();
129+
}
127130
if (platformSupportsStreaming() && !wrappingTarget.__sentry_test_doesnt_support_streaming__) {
128131
target.apply(thisArg, argArray);
129132
} else {
@@ -179,8 +182,10 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri
179182
res.statusCode = 500;
180183
res.statusMessage = 'Internal Server Error';
181184

182-
span?.setHttpStatus(res.statusCode);
183-
span?.end();
185+
if (span) {
186+
setHttpStatus(span, res.statusCode);
187+
span.end();
188+
}
184189

185190
// Make sure we have a chance to finish the transaction and flush events to Sentry before the handler errors
186191
// out. (Apps which are deployed on Vercel run their API routes in lambdas, and those lambdas will shut down the

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
getCurrentScope,
55
handleCallbackErrors,
66
runWithAsyncContext,
7+
setHttpStatus,
78
startSpan,
89
} from '@sentry/core';
910
import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
@@ -66,7 +67,7 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
6667
);
6768

6869
try {
69-
span?.setHttpStatus(response.status);
70+
span && setHttpStatus(span, response.status);
7071
} catch {
7172
// best effort
7273
}

0 commit comments

Comments
 (0)