Skip to content

Commit

Permalink
ref(node): Move request-data-extraction functions to@sentry/utils (#…
Browse files Browse the repository at this point in the history
…5257)

(See #5190 for a detailed description of the motivation for and main substance of this change. TL;DR, isomorphic frameworks like Nextjs need this code to live in a neutral location (rather than in the node package) so it can be used in a browser setting as well.)

This is a second take on #5190, which had to be reverted because it relied on a peer dependency (`cookie`) which browser-only apps wouldn't have installed. Even if those code didn't _use_ `cookie`, they still failed to compile because without `cookie` installed, `@sentry/utils` didn't typecheck correctly.

This fixes that problem by using `cookie` (and `url`, for node 8 compatibility) only as injected dependencies, not as direct ones. It also creates node-specific versions of the relevant functions (`parseRequest`, now renamed `addRequestDataToEvent`, and `extractRequestData`) which do the injection automatically. If the dependencies aren't passed (as would be the case in a browser setting, or when using the functions directly from `@sentry/utils`), the code about cookies no-ops, and the code about URLs uses `URL`, which should be defined in all modern browsers and Node 10 and above.

Other changes from the original PR:

- Now only the backwards-compatibility-preserving wrappers of the legacy code live in `handlers.ts`, while the legacy code itself lives in its own file. This both makes it easier to delete in the future and ensures that treeshaking algorithms which only work file-by-file (rather than function-by-function) are able to exclude that code. (Though it's being kept until v8 because it's part of the node package's public API, it's no longer used anywhere in the SDK.)
- Using DI changed the options interface for both of the functions in question, which in turn required a bit more gymnastics in terms of preserving and test backwards compatibility, specifically in the serverless package and the backwards-compatibility tests. This will make it a little harder to delete the old stuff when it comes time, but there are TODOs which hopefully will make it clear enough what needs to happen.
- There's a new `CrossPlatformRequest` type for use in place of `ExpressRequest`. A few helper functions have also been renamed to make them less Express-centric.
  • Loading branch information
lobsterkatie authored Jun 15, 2022
1 parent 7bd7170 commit d69cafc
Show file tree
Hide file tree
Showing 20 changed files with 1,202 additions and 745 deletions.
6 changes: 2 additions & 4 deletions packages/nextjs/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable max-lines */
import {
addRequestDataToEvent,
captureException,
configureScope,
deepReadDirSync,
getCurrentHub,
Handlers,
startTransaction,
} from '@sentry/node';
import { extractTraceparentData, getActiveTransaction, hasTracingEnabled } from '@sentry/tracing';
Expand All @@ -22,8 +22,6 @@ import { default as createNextServer } from 'next';
import * as querystring from 'querystring';
import * as url from 'url';

const { parseRequest } = Handlers;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type PlainObject<T = any> = { [key: string]: T };

Expand Down Expand Up @@ -246,7 +244,7 @@ function makeWrappedReqHandler(origReqHandler: ReqHandler): WrappedReqHandler {
const currentScope = getCurrentHub().getScope();

if (currentScope) {
currentScope.addEventProcessor(event => parseRequest(event, nextReq));
currentScope.addEventProcessor(event => addRequestDataToEvent(event, nextReq));

// We only want to record page and API requests
if (hasTracingEnabled() && shouldTraceRequest(nextReq.url, publicDirFiles)) {
Expand Down
6 changes: 2 additions & 4 deletions packages/nextjs/src/utils/withSentry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { addRequestDataToEvent, captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { extractTraceparentData, hasTracingEnabled } from '@sentry/tracing';
import { Transaction } from '@sentry/types';
import {
Expand All @@ -12,8 +12,6 @@ import {
import * as domain from 'domain';
import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next';

const { parseRequest } = Handlers;

// This is the same as the `NextApiHandler` type, except instead of having a return type of `void | Promise<void>`, it's
// only `Promise<void>`, because wrapped handlers are always async
export type WrappedNextApiHandler = (req: NextApiRequest, res: NextApiResponse) => Promise<void>;
Expand Down Expand Up @@ -43,7 +41,7 @@ export const withSentry = (origHandler: NextApiHandler): WrappedNextApiHandler =
const currentScope = getCurrentHub().getScope();

if (currentScope) {
currentScope.addEventProcessor(event => parseRequest(event, req));
currentScope.addEventProcessor(event => addRequestDataToEvent(event, req));

if (hasTracingEnabled()) {
// If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
Expand Down
13 changes: 10 additions & 3 deletions packages/node/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BaseClient, Scope, SDK_VERSION } from '@sentry/core';
import { SessionFlusher } from '@sentry/hub';
import { Event, EventHint, Severity, SeverityLevel } from '@sentry/types';
import { logger, resolvedSyncPromise } from '@sentry/utils';
import * as os from 'os';
import { TextEncoder } from 'util';

import { eventFromMessage, eventFromUnknownInput } from './eventbuilder';
Expand Down Expand Up @@ -139,9 +140,15 @@ export class NodeClient extends BaseClient<NodeClientOptions> {
*/
protected _prepareEvent(event: Event, hint: EventHint, scope?: Scope): PromiseLike<Event | null> {
event.platform = event.platform || 'node';
if (this.getOptions().serverName) {
event.server_name = this.getOptions().serverName;
}
event.contexts = {
...event.contexts,
runtime: event.contexts?.runtime || {
name: 'node',
version: global.process.version,
},
};
event.server_name =
event.server_name || this.getOptions().serverName || global.process.env.SENTRY_NAME || os.hostname();
return super._prepareEvent(event, hint, scope);
}

Expand Down
Loading

0 comments on commit d69cafc

Please sign in to comment.