Skip to content

Commit e73c241

Browse files
committed
generalized cleanup
1 parent a0bab2f commit e73c241

File tree

3 files changed

+44
-40
lines changed

3 files changed

+44
-40
lines changed

packages/nextjs/src/index.server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export function init(options: NextjsOptions): void {
1717
const metadataBuilder = new MetadataBuilder(options, ['nextjs', 'node']);
1818
metadataBuilder.addSdkMetadata();
1919
options.environment = options.environment || process.env.NODE_ENV;
20+
// TODO capture project root and store in an env var for RewriteFrames?
2021
addServerIntegrations(options);
2122
// Right now we only capture frontend sessions for Next.js
2223
options.autoSessionTracking = false;
@@ -47,5 +48,5 @@ function addServerIntegrations(options: NextjsOptions): void {
4748
export { withSentryConfig } from './utils/config';
4849
export { withSentry } from './utils/handlers';
4950

50-
// TODO capture project root (which this returns) for RewriteFrames?
51+
// wrap various server methods to enable error monitoring and tracing
5152
instrumentServer();

packages/nextjs/src/utils/instrumentServer.ts

Lines changed: 39 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { deepReadDirSync } from '@sentry/node';
22
import { getActiveTransaction, hasTracingEnabled } from '@sentry/tracing';
3-
import { Event as SentryEvent, Transaction } from '@sentry/types';
3+
import { Event as SentryEvent } from '@sentry/types';
44
import { fill, logger } from '@sentry/utils';
55
import * as domain from 'domain';
66
import * as http from 'http';
@@ -33,22 +33,18 @@ interface NextRequest extends http.IncomingMessage {
3333
query: { [key: string]: string };
3434
headers: { [key: string]: string };
3535
}
36+
type NextResponse = http.ServerResponse;
3637

37-
interface NextResponse extends http.ServerResponse {
38-
__sentry__: {
39-
transaction?: Transaction;
40-
};
41-
}
42-
38+
// the methods we'll wrap
4339
type HandlerGetter = () => Promise<ReqHandler>;
4440
type ReqHandler = (req: NextRequest, res: NextResponse, parsedUrl?: url.UrlWithParsedQuery) => Promise<void>;
4541
type ErrorLogger = (err: Error) => void;
4642
type ApiPageEnsurer = (path: string) => Promise<void>;
4743
type PageComponentFinder = (
4844
pathname: string,
4945
query: querystring.ParsedUrlQuery,
50-
params: { [key: string]: any } | null,
51-
) => Promise<{ [key: string]: any } | null>;
46+
params: { [key: string]: unknown } | null,
47+
) => Promise<{ [key: string]: unknown } | null>;
5248

5349
// these aliases are purely to make the function signatures more easily understandable
5450
type WrappedHandlerGetter = HandlerGetter;
@@ -57,19 +53,14 @@ type WrappedReqHandler = ReqHandler;
5753
type WrappedApiPageEnsurer = ApiPageEnsurer;
5854
type WrappedPageComponentFinder = PageComponentFinder;
5955

60-
// TODO is it necessary for this to be an object?
61-
const closure: PlainObject = {};
62-
56+
let liveServer: Server;
6357
let sdkSetupComplete = false;
6458

6559
/**
6660
* Do the monkeypatching and wrapping necessary to catch errors in page routes and record transactions for both page and
67-
* API routes. Along the way, as a bonus, grab (and return) the path of the project root, for use in `RewriteFrames`.
68-
*
69-
* @returns The absolute path of the project root directory
70-
*
61+
* API routes.
7162
*/
72-
export function instrumentServer(): string {
63+
export function instrumentServer(): void {
7364
// The full implementation here involves a lot of indirection and multiple layers of callbacks and wrapping, and is
7465
// therefore potentially a little hard to follow. Here's the overall idea:
7566

@@ -78,26 +69,34 @@ export function instrumentServer(): string {
7869
// `createNextServer()`, which returns a `NextServer` instance.
7970

8071
// At server startup:
81-
// `next.config.js` imports SDK -> SDK index.ts -> `instrumentServer()` (the function we're in right now) ->
82-
// `createNextServer()` -> `NextServer` instance -> `NextServer` prototype -> wrap
83-
// `NextServer.getServerRequestHandler()`, purely to get us to the next step
72+
// `next.config.js` imports SDK ->
73+
// SDK's `index.ts` runs ->
74+
// `instrumentServer()` (the function we're in right now) ->
75+
// `createNextServer()` ->
76+
// `NextServer` instance ->
77+
// `NextServer` prototype ->
78+
// Wrap `NextServer.getServerRequestHandler()`, purely to get us to the next step
8479

8580
// At time of first request:
86-
// Wrapped `getServerRequestHandler` runs for the first time -> live `NextServer` instance (via `this`) -> live
87-
// `Server` instance -> `Server` prototype -> wrap `Server.logError` and `Server.handleRequest` methods, then pass
88-
// wrapped version of `handleRequest` to caller of `getServerRequestHandler`
81+
// Wrapped `getServerRequestHandler` runs for the first time ->
82+
// Live `NextServer` instance(via`this`) ->
83+
// Live `Server` instance (via `NextServer.server`) ->
84+
// `Server` prototype ->
85+
// Wrap `Server.logError`, `Server.handleRequest`, `Server.ensureApiPage`, and `Server.findPageComponents` methods,
86+
// then fulfill original purpose of function by passing wrapped version of `handleRequest` to caller
8987

9088
// Whenever caller of `NextServer.getServerRequestHandler` calls the wrapped `Server.handleRequest`:
91-
// Trace request
89+
// Trace request
9290

9391
// Whenever something calls the wrapped `Server.logError`:
94-
// Capture error
92+
// Capture error
93+
94+
// Whenever an API request is handled and the wrapped `Server.ensureApiPage` is called, or whenever a page request is
95+
// handled and the wrapped `Server.findPageComponents` is called:
96+
// Replace URL in transaction name with parameterized version
9597

9698
const nextServerPrototype = Object.getPrototypeOf(createNextServer({}));
9799
fill(nextServerPrototype, 'getServerRequestHandler', makeWrappedHandlerGetter);
98-
99-
// TODO replace with an env var, since at this point we don't have a value yet
100-
return closure.projectRootDir;
101100
}
102101

103102
/**
@@ -122,17 +121,14 @@ function makeWrappedHandlerGetter(origHandlerGetter: HandlerGetter): WrappedHand
122121
logger.error(`[Sentry] Could not initialize SDK. Received error:\n${err}`);
123122
}
124123

125-
// TODO: Replace projectRootDir with env variables
126-
closure.projectRootDir = this.server.dir;
127-
closure.server = this.server;
128-
closure.publicDir = this.server.publicDir;
129-
130-
const serverPrototype = Object.getPrototypeOf(this.server);
124+
// stash this in the closure so that `makeWrappedReqHandler` can use it
125+
liveServer = this.server;
126+
const serverPrototype = Object.getPrototypeOf(liveServer);
131127

132-
// wrap for error capturing (`logError` gets called by `next` for all server-side errors)
128+
// Wrap for error capturing (`logError` gets called by `next` for all server-side errors)
133129
fill(serverPrototype, 'logError', makeWrappedErrorLogger);
134130

135-
// wrap for request transaction creation (`handleRequest` is called for all incoming requests, and dispatches them
131+
// Wrap for request transaction creation (`handleRequest` is called for all incoming requests, and dispatches them
136132
// to the appropriate handlers)
137133
fill(serverPrototype, 'handleRequest', makeWrappedReqHandler);
138134

@@ -172,8 +168,6 @@ function makeWrappedErrorLogger(origErrorLogger: ErrorLogger): WrappedErrorLogge
172168
* @returns A wrapped version of that handler
173169
*/
174170
function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
175-
const liveServer = closure.server as Server;
176-
177171
// inspired by next's public file routing; see
178172
// https://github.com/vercel/next.js/blob/4443d6f3d36b107e833376c2720c1e206eee720d/packages/next/next-server/server/next-server.ts#L1166
179173
const publicDirFiles = new Set(
@@ -286,6 +280,13 @@ function shouldTraceRequest(url: string, publicDirFiles: Set<string>): boolean {
286280
return !url.startsWith('/_next/') && !url.startsWith('/static/') && !publicDirFiles.has(url);
287281
}
288282

283+
/**
284+
* Harvest specific data from the request, and add it to the event.
285+
*
286+
* @param event The event to which to add request data
287+
* @param req The request whose data is being added
288+
* @returns The modified event
289+
*/
289290
function addRequestDataToEvent(event: SentryEvent, req: NextRequest): SentryEvent {
290291
event.request = {
291292
...event.request,

packages/node/src/integrations/http.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ function _createWrappedRequestMethodFactory(
116116
});
117117

118118
const sentryTraceHeader = span.toTraceparent();
119-
logger.log(`[Tracing] Adding sentry-trace header to outgoing request: ${sentryTraceHeader}`);
119+
logger.log(
120+
`[Tracing] Adding sentry-trace header ${sentryTraceHeader} to outgoing request to ${requestUrl}: `,
121+
);
120122
requestOptions.headers = { ...requestOptions.headers, 'sentry-trace': sentryTraceHeader };
121123
}
122124
}

0 commit comments

Comments
 (0)