Skip to content

Commit d882819

Browse files
committed
generalized cleanup
1 parent 40bab14 commit d882819

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
@@ -19,6 +19,7 @@ export function init(options: NextjsOptions): void {
1919
if (options.integrations) {
2020
options.integrations = getFinalServerIntegrations(options.integrations);
2121
} else {
22+
// TODO capture project root and store in an env var for RewriteFrames?
2223
options.integrations = [defaultRewriteFrames];
2324
}
2425

@@ -34,5 +35,5 @@ export function init(options: NextjsOptions): void {
3435
export { withSentryConfig } from './utils/config';
3536
export { withSentry } from './utils/handlers';
3637

37-
// TODO capture project root (which this returns) for RewriteFrames?
38+
// wrap various server methods to enable error monitoring and tracing
3839
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';
@@ -32,22 +32,18 @@ interface NextRequest extends http.IncomingMessage {
3232
url: string;
3333
query: { [key: string]: string };
3434
}
35+
type NextResponse = http.ServerResponse;
3536

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

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

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

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

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

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

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

8987
// Whenever caller of `NextServer.getServerRequestHandler` calls the wrapped `Server.handleRequest`:
90-
// Trace request
88+
// Trace request
9189

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

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

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

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

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

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

@@ -171,8 +167,6 @@ function makeWrappedErrorLogger(origErrorLogger: ErrorLogger): WrappedErrorLogge
171167
* @returns A wrapped version of that handler
172168
*/
173169
function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
174-
const liveServer = closure.server as Server;
175-
176170
// inspired by next's public file routing; see
177171
// https://github.com/vercel/next.js/blob/4443d6f3d36b107e833376c2720c1e206eee720d/packages/next/next-server/server/next-server.ts#L1166
178172
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
// TODO body/data

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)