Skip to content

Commit c9f78ce

Browse files
committed
improve documentation
1 parent de075a4 commit c9f78ce

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

packages/nextjs/src/utils/instrumentServer.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,15 @@ import * as Sentry from '../index.server';
1111
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1212
type PlainObject<T = any> = { [key: string]: T };
1313

14+
// class used by `next` as a proxy to the real server; see
15+
// https://github.com/vercel/next.js/blob/4443d6f3d36b107e833376c2720c1e206eee720d/packages/next/server/next.ts#L32
1416
interface NextServer {
1517
server: Server;
1618
createServer: (options: PlainObject) => Server;
1719
}
1820

21+
// `next`'s main server class; see
22+
// https://github.com/vercel/next.js/blob/4443d6f3d36b107e833376c2720c1e206eee720d/packages/next/next-server/server/next-server.ts#L132
1923
interface Server {
2024
dir: string;
2125
publicDir: string;
@@ -47,17 +51,37 @@ const closure: PlainObject = {};
4751
let sdkSetupComplete = false;
4852

4953
/**
50-
* Do the monkeypatching and wrapping necessary to catch errors in page routes. Along the way, as a bonus, grab (and
51-
* return) the path of the project root, for use in `RewriteFrames`.
54+
* Do the monkeypatching and wrapping necessary to catch errors in page routes and record transactions for both page and
55+
* API routes. Along the way, as a bonus, grab (and return) the path of the project root, for use in `RewriteFrames`.
5256
*
5357
* @returns The absolute path of the project root directory
5458
*
5559
*/
5660
export function instrumentServer(): string {
57-
const nextServerPrototype = Object.getPrototypeOf(createNextServer({}));
61+
// The full implementation here involves a lot of indirection and multiple layers of callbacks and wrapping, and is
62+
// therefore potentially a little hard to follow. Here's the overall idea:
63+
64+
// Next.js uses two server classes, `NextServer` and `Server`, with the former proxying calls to the latter, which
65+
// then does the all real work. The only access we have to either is through Next's default export,
66+
// `createNextServer()`, which returns a `NextServer` instance.
67+
68+
// At server startup:
69+
// `next.config.js` imports SDK -> SDK index.ts -> `instrumentServer()` (the function we're in right now) ->
70+
// `createNextServer()` -> `NextServer` instance -> `NextServer` prototype -> wrap
71+
// `NextServer.getServerRequestHandler()`, purely to get us to the next step
72+
73+
// At time of first request:
74+
// Wrapped `getServerRequestHandler` runs for the first time -> live `NextServer` instance (via `this`) -> live
75+
// `Server` instance -> `Server` prototype -> wrap `Server.logError` and `Server.handleRequest` methods, then pass
76+
// wrapped version of `handleRequest` to caller of `getServerRequestHandler`
5877

59-
// wrap this getter because it runs before the request handler runs, which gives us a chance to wrap the logger before
60-
// it's called for the first time
78+
// Whenever caller of `NextServer.getServerRequestHandler` calls the wrapped `Server.handleRequest`:
79+
// Trace request
80+
81+
// Whenever something calls the wrapped `Server.logError`:
82+
// Capture error
83+
84+
const nextServerPrototype = Object.getPrototypeOf(createNextServer({}));
6185
fill(nextServerPrototype, 'getServerRequestHandler', makeWrappedHandlerGetter);
6286

6387
// TODO replace with an env var, since at this point we don't have a value yet
@@ -93,9 +117,11 @@ function makeWrappedHandlerGetter(origHandlerGetter: HandlerGetter): WrappedHand
93117

94118
const serverPrototype = Object.getPrototypeOf(this.server);
95119

96-
// wrap the logger so we can capture errors in page-level functions like `getServerSideProps`
120+
// wrap for error capturing (`logError` gets called by `next` for all server-side errors)
97121
fill(serverPrototype, 'logError', makeWrappedErrorLogger);
98122

123+
// wrap for request transaction creation (`handleRequest` is called for all incoming requests, and dispatches them
124+
// to the appropriate handlers)
99125
fill(serverPrototype, 'handleRequest', makeWrappedReqHandler);
100126

101127
sdkSetupComplete = true;

0 commit comments

Comments
 (0)