-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
trpc.ts
94 lines (81 loc) · 2.76 KB
/
trpc.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { normalize } from '@sentry/utils';
import { getClient } from './currentScopes';
import { captureException, setContext } from './exports';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from './semanticAttributes';
import { startSpanManual } from './tracing';
interface SentryTrpcMiddlewareOptions {
/** Whether to include procedure inputs in reported events. Defaults to `false`. */
attachRpcInput?: boolean;
}
export interface SentryTrpcMiddlewareArguments<T> {
path?: unknown;
type?: unknown;
next: () => T;
rawInput?: unknown;
getRawInput?: () => Promise<unknown>;
}
const trpcCaptureContext = { mechanism: { handled: false, data: { function: 'trpcMiddleware' } } };
function captureIfError(nextResult: unknown): void {
// TODO: Set span status based on what TRPCError was encountered
if (
typeof nextResult === 'object' &&
nextResult !== null &&
'ok' in nextResult &&
!nextResult.ok &&
'error' in nextResult
) {
captureException(nextResult.error, trpcCaptureContext);
}
}
type SentryTrpcMiddleware<T> = T extends Promise<unknown> ? T : Promise<T>;
/**
* Sentry tRPC middleware that captures errors and creates spans for tRPC procedures.
*/
export function trpcMiddleware(options: SentryTrpcMiddlewareOptions = {}) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return async function <T>(opts: SentryTrpcMiddlewareArguments<T>): SentryTrpcMiddleware<T> {
const { path, type, next, rawInput, getRawInput } = opts;
const client = getClient();
const clientOptions = client && client.getOptions();
const trpcContext: Record<string, unknown> = {
procedure_type: type,
};
if (options.attachRpcInput !== undefined ? options.attachRpcInput : clientOptions && clientOptions.sendDefaultPii) {
if (rawInput !== undefined) {
trpcContext.input = normalize(rawInput);
}
if (getRawInput !== undefined && typeof getRawInput === 'function') {
try {
const rawRes = await getRawInput();
trpcContext.input = normalize(rawRes);
} catch (err) {
// noop
}
}
}
setContext('trpc', trpcContext);
return startSpanManual(
{
name: `trpc/${path}`,
op: 'rpc.server',
attributes: {
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.rpc.trpc',
},
},
async span => {
try {
const nextResult = await next();
captureIfError(nextResult);
span.end();
return nextResult;
} catch (e) {
captureException(e, trpcCaptureContext);
span.end();
throw e;
}
},
) as SentryTrpcMiddleware<T>;
};
}