@senzops/apm-node 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/worktrees/infallible-chatelet-f3fb36/.claude/settings.local.json +9 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/CHANGELOG.md +49 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/README.md +398 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/package-lock.json +1494 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/package.json +42 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/client.ts +451 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/context.ts +48 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/normalizer.ts +44 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/sanitizer.ts +203 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/transport.ts +273 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/core/types.ts +106 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/index.ts +36 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/bullmq.ts +195 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/cron.ts +204 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/express.ts +338 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/fastify.ts +296 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/framework.ts +301 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/hook.ts +134 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/http.ts +530 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/koa.ts +173 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/mongo.ts +202 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/mongoose.ts +156 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/mysql.ts +169 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/patch.ts +56 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/pg.ts +131 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/redis.ts +109 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/span.ts +73 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/instrumentation/undici.ts +189 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/middleware/express.ts +48 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/register.ts +58 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/utils/getClientIp.ts +175 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/utils/ids.ts +7 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/utils/internal.ts +1 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/utils/sdkMeta.ts +6 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/utils/traceContext.ts +44 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/wrappers/fastify.ts +35 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/wrappers/h3.ts +59 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/src/wrappers/next.ts +131 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/tsconfig.json +15 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/tsup.config.ts +21 -0
- package/.claude/worktrees/infallible-chatelet-f3fb36/wiki.md +852 -0
- package/CHANGELOG.md +4 -0
- package/dist/index.global.js +1 -1
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/register.js +1 -1
- package/dist/register.js.map +1 -1
- package/dist/register.mjs +1 -1
- package/dist/register.mjs.map +1 -1
- package/package.json +1 -1
- package/src/instrumentation/hook.ts +85 -216
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { SenzorOptions } from '../core/types';
|
|
2
|
+
import { hookRequire } from './hook';
|
|
3
|
+
import { patchMethod } from './patch';
|
|
4
|
+
import { wrapFrameworkHandlerWithArity } from './framework';
|
|
5
|
+
|
|
6
|
+
const FACTORY_PATCHED = Symbol.for('senzor.fastify.factory.patched');
|
|
7
|
+
const INSTANCE_PATCHED = Symbol.for('senzor.fastify.instance.patched');
|
|
8
|
+
|
|
9
|
+
const lifecycleHookNames = new Set([
|
|
10
|
+
'onRequest',
|
|
11
|
+
'preParsing',
|
|
12
|
+
'preValidation',
|
|
13
|
+
'preHandler',
|
|
14
|
+
'preSerialization',
|
|
15
|
+
'onSend',
|
|
16
|
+
'onResponse',
|
|
17
|
+
'onError',
|
|
18
|
+
'onTimeout',
|
|
19
|
+
'onRequestAbort'
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
const routeLifecycleKeys = [
|
|
23
|
+
'onRequest',
|
|
24
|
+
'preParsing',
|
|
25
|
+
'preValidation',
|
|
26
|
+
'preHandler',
|
|
27
|
+
'preSerialization',
|
|
28
|
+
'onSend',
|
|
29
|
+
'onResponse',
|
|
30
|
+
'onError'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const getRoute = (request: any, fallback?: string): string | undefined =>
|
|
34
|
+
request?.routeOptions?.url ||
|
|
35
|
+
request?.routerPath ||
|
|
36
|
+
request?.context?.config?.url ||
|
|
37
|
+
fallback;
|
|
38
|
+
|
|
39
|
+
const wrapHook = (
|
|
40
|
+
hookName: string,
|
|
41
|
+
handler: any,
|
|
42
|
+
options?: SenzorOptions,
|
|
43
|
+
route?: string
|
|
44
|
+
) => {
|
|
45
|
+
if (typeof handler !== 'function') return handler;
|
|
46
|
+
|
|
47
|
+
return wrapFrameworkHandlerWithArity(
|
|
48
|
+
handler,
|
|
49
|
+
(_thisArg, args) => {
|
|
50
|
+
const request = args[0];
|
|
51
|
+
const reply = args[1];
|
|
52
|
+
const currentRoute = getRoute(request, route);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
framework: 'fastify',
|
|
56
|
+
type: hookName === 'onError' ? 'error_handler' : 'lifecycle_hook',
|
|
57
|
+
name: `fastify.${hookName} ${currentRoute || request?.url || ''}`.trim(),
|
|
58
|
+
route: currentRoute,
|
|
59
|
+
method: request?.method,
|
|
60
|
+
handlerName: handler.name || hookName,
|
|
61
|
+
request,
|
|
62
|
+
response: reply?.raw || reply,
|
|
63
|
+
attributes: {
|
|
64
|
+
'fastify.hook': hookName,
|
|
65
|
+
'fastify.type': hookName === 'onError' ? 'error_handler' : 'lifecycle_hook',
|
|
66
|
+
'http.route': currentRoute,
|
|
67
|
+
url: request?.url
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
options,
|
|
72
|
+
{
|
|
73
|
+
callbackCompletesSpan: true,
|
|
74
|
+
responseEndsSpan: false
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const wrapRouteHandler = (
|
|
80
|
+
handler: any,
|
|
81
|
+
options?: SenzorOptions,
|
|
82
|
+
route?: string
|
|
83
|
+
) => {
|
|
84
|
+
if (typeof handler !== 'function') return handler;
|
|
85
|
+
|
|
86
|
+
return wrapFrameworkHandlerWithArity(
|
|
87
|
+
handler,
|
|
88
|
+
(_thisArg, args) => {
|
|
89
|
+
const request = args[0];
|
|
90
|
+
const reply = args[1];
|
|
91
|
+
const currentRoute = getRoute(request, route);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
framework: 'fastify',
|
|
95
|
+
type: 'route_handler',
|
|
96
|
+
name: `fastify.route_handler ${request?.method || ''} ${currentRoute || request?.url || ''}`.trim(),
|
|
97
|
+
route: currentRoute,
|
|
98
|
+
method: request?.method,
|
|
99
|
+
handlerName: handler.name || 'handler',
|
|
100
|
+
request,
|
|
101
|
+
response: reply?.raw || reply,
|
|
102
|
+
attributes: {
|
|
103
|
+
'fastify.type': 'route_handler',
|
|
104
|
+
'http.route': currentRoute,
|
|
105
|
+
url: request?.url
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
options,
|
|
110
|
+
{
|
|
111
|
+
callbackCompletesSpan: true,
|
|
112
|
+
responseEndsSpan: true
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const wrapMaybeArray = (
|
|
118
|
+
value: any,
|
|
119
|
+
wrap: (handler: any) => any
|
|
120
|
+
) => {
|
|
121
|
+
if (Array.isArray(value)) return value.map(wrap);
|
|
122
|
+
return wrap(value);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const patchFastifyInstance = (
|
|
126
|
+
instance: any,
|
|
127
|
+
options?: SenzorOptions
|
|
128
|
+
) => {
|
|
129
|
+
if (!instance || instance[INSTANCE_PATCHED]) return instance;
|
|
130
|
+
|
|
131
|
+
Object.defineProperty(instance, INSTANCE_PATCHED, {
|
|
132
|
+
value: true,
|
|
133
|
+
enumerable: false
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
patchMethod(
|
|
137
|
+
instance,
|
|
138
|
+
'addHook',
|
|
139
|
+
'senzor.fastify.addHook',
|
|
140
|
+
(original) =>
|
|
141
|
+
function patchedFastifyAddHook(this: any, hookName: string, handler: any) {
|
|
142
|
+
if (lifecycleHookNames.has(hookName)) {
|
|
143
|
+
return original.call(this, hookName, wrapHook(hookName, handler, options));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return original.apply(this, arguments as any);
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
patchMethod(
|
|
151
|
+
instance,
|
|
152
|
+
'route',
|
|
153
|
+
'senzor.fastify.route',
|
|
154
|
+
(original) =>
|
|
155
|
+
function patchedFastifyRoute(this: any, routeOptions: any) {
|
|
156
|
+
if (!routeOptions || typeof routeOptions !== 'object') {
|
|
157
|
+
return original.apply(this, arguments as any);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const nextRouteOptions = { ...routeOptions };
|
|
161
|
+
const route =
|
|
162
|
+
nextRouteOptions.url ||
|
|
163
|
+
nextRouteOptions.path ||
|
|
164
|
+
nextRouteOptions.routePath;
|
|
165
|
+
|
|
166
|
+
if (nextRouteOptions.handler) {
|
|
167
|
+
nextRouteOptions.handler = wrapRouteHandler(
|
|
168
|
+
nextRouteOptions.handler,
|
|
169
|
+
options,
|
|
170
|
+
route
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (const key of routeLifecycleKeys) {
|
|
175
|
+
if (nextRouteOptions[key]) {
|
|
176
|
+
nextRouteOptions[key] = wrapMaybeArray(
|
|
177
|
+
nextRouteOptions[key],
|
|
178
|
+
(handler) => wrapHook(key, handler, options, route)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return original.call(this, nextRouteOptions);
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
patchMethod(
|
|
188
|
+
instance,
|
|
189
|
+
'setErrorHandler',
|
|
190
|
+
'senzor.fastify.setErrorHandler',
|
|
191
|
+
(original) =>
|
|
192
|
+
function patchedFastifySetErrorHandler(this: any, handler: any) {
|
|
193
|
+
return original.call(
|
|
194
|
+
this,
|
|
195
|
+
wrapFrameworkHandlerWithArity(
|
|
196
|
+
handler,
|
|
197
|
+
(_thisArg, args) => {
|
|
198
|
+
const request = args[1];
|
|
199
|
+
const reply = args[2];
|
|
200
|
+
const route = getRoute(request);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
framework: 'fastify',
|
|
204
|
+
type: 'error_handler',
|
|
205
|
+
name: `fastify.error_handler ${route || request?.url || ''}`.trim(),
|
|
206
|
+
route,
|
|
207
|
+
method: request?.method,
|
|
208
|
+
handlerName: handler?.name || 'errorHandler',
|
|
209
|
+
request,
|
|
210
|
+
response: reply?.raw || reply,
|
|
211
|
+
attributes: {
|
|
212
|
+
'fastify.type': 'error_handler',
|
|
213
|
+
error: args[0]?.message,
|
|
214
|
+
'error.type': args[0]?.name || typeof args[0]
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
},
|
|
218
|
+
options,
|
|
219
|
+
{
|
|
220
|
+
callbackCompletesSpan: true,
|
|
221
|
+
responseEndsSpan: true
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return instance;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const copyFactoryProperties = (
|
|
232
|
+
source: any,
|
|
233
|
+
target: any
|
|
234
|
+
) => {
|
|
235
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
236
|
+
if (['length', 'name', 'prototype'].includes(String(key))) continue;
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)!);
|
|
240
|
+
} catch { }
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const wrapFastifyFactory = (
|
|
245
|
+
factory: any,
|
|
246
|
+
options?: SenzorOptions
|
|
247
|
+
) => {
|
|
248
|
+
if (typeof factory !== 'function' || factory[FACTORY_PATCHED]) {
|
|
249
|
+
return factory;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const wrapped = function senzorFastifyFactory(this: unknown, ...args: any[]) {
|
|
253
|
+
const instance = factory.apply(this, args);
|
|
254
|
+
return patchFastifyInstance(instance, options);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
copyFactoryProperties(factory, wrapped);
|
|
258
|
+
|
|
259
|
+
Object.defineProperty(wrapped, FACTORY_PATCHED, {
|
|
260
|
+
value: true,
|
|
261
|
+
enumerable: false
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const mutableWrapped = wrapped as any;
|
|
265
|
+
|
|
266
|
+
if (mutableWrapped.fastify === factory) {
|
|
267
|
+
mutableWrapped.fastify = mutableWrapped;
|
|
268
|
+
}
|
|
269
|
+
if (mutableWrapped.default === factory) {
|
|
270
|
+
mutableWrapped.default = mutableWrapped;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return mutableWrapped;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
export const instrumentFastify = (options?: SenzorOptions) => {
|
|
277
|
+
hookRequire('fastify', (exports: any) => {
|
|
278
|
+
if (typeof exports === 'function') {
|
|
279
|
+
return wrapFastifyFactory(exports, options);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (exports?.fastify) {
|
|
283
|
+
exports.fastify = wrapFastifyFactory(exports.fastify, options);
|
|
284
|
+
}
|
|
285
|
+
if (exports?.default) {
|
|
286
|
+
exports.default = wrapFastifyFactory(exports.default, options);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return exports;
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
export const instrumentFastifyInstance = (
|
|
294
|
+
instance: any,
|
|
295
|
+
options?: SenzorOptions
|
|
296
|
+
) => patchFastifyInstance(instance, options);
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { Context } from '../core/context';
|
|
2
|
+
import { SenzorOptions } from '../core/types';
|
|
3
|
+
import { CapturedSpan, runWithCapturedSpan, startCapturedSpan } from './span';
|
|
4
|
+
|
|
5
|
+
export type FrameworkSpanType =
|
|
6
|
+
| 'middleware'
|
|
7
|
+
| 'router'
|
|
8
|
+
| 'request_handler'
|
|
9
|
+
| 'route_handler'
|
|
10
|
+
| 'controller_handler'
|
|
11
|
+
| 'lifecycle_hook'
|
|
12
|
+
| 'error_handler'
|
|
13
|
+
| 'event_handler';
|
|
14
|
+
|
|
15
|
+
export interface FrameworkSpanInfo {
|
|
16
|
+
framework: string;
|
|
17
|
+
type: FrameworkSpanType;
|
|
18
|
+
name: string;
|
|
19
|
+
route?: string;
|
|
20
|
+
method?: string;
|
|
21
|
+
layerPath?: string;
|
|
22
|
+
handlerName?: string;
|
|
23
|
+
request?: any;
|
|
24
|
+
response?: any;
|
|
25
|
+
attributes?: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface InvokeOptions {
|
|
29
|
+
callbackIndex?: number;
|
|
30
|
+
callbackCompletesSpan?: boolean;
|
|
31
|
+
responseEndsSpan?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const ignoredNextValues = new Set([undefined, null, 'route', 'router']);
|
|
35
|
+
|
|
36
|
+
export const shouldCaptureFrameworkSpan = (
|
|
37
|
+
type: FrameworkSpanType,
|
|
38
|
+
options?: SenzorOptions
|
|
39
|
+
): boolean => {
|
|
40
|
+
if (options?.frameworkSpans === false) return false;
|
|
41
|
+
if (type === 'middleware' && options?.captureMiddlewareSpans === false) return false;
|
|
42
|
+
if (type === 'router' && options?.captureRouterSpans === false) return false;
|
|
43
|
+
if (type === 'lifecycle_hook' && options?.captureLifecycleHookSpans === false) return false;
|
|
44
|
+
if (options?.ignoreFrameworkSpanTypes?.includes(type)) return false;
|
|
45
|
+
return true;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const statusFrom = (
|
|
49
|
+
info: FrameworkSpanInfo,
|
|
50
|
+
fallback = 0
|
|
51
|
+
): number => {
|
|
52
|
+
const res = info.response;
|
|
53
|
+
return (
|
|
54
|
+
res?.statusCode ||
|
|
55
|
+
res?.status ||
|
|
56
|
+
res?.raw?.statusCode ||
|
|
57
|
+
res?.status_code ||
|
|
58
|
+
fallback
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const isPromiseLike = (value: unknown): value is Promise<unknown> =>
|
|
63
|
+
Boolean(value && typeof (value as any).then === 'function');
|
|
64
|
+
|
|
65
|
+
const runWithParentOf = <T>(
|
|
66
|
+
span: CapturedSpan,
|
|
67
|
+
fn: () => T
|
|
68
|
+
): T => {
|
|
69
|
+
if (!span.parentSpanId) return fn();
|
|
70
|
+
return Context.withActiveSpan(span.parentSpanId, fn);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const copyFunctionProperties = (
|
|
74
|
+
source: Function,
|
|
75
|
+
target: Function
|
|
76
|
+
) => {
|
|
77
|
+
for (const key in source as any) {
|
|
78
|
+
try {
|
|
79
|
+
Object.defineProperty(target, key, {
|
|
80
|
+
configurable: true,
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get() {
|
|
83
|
+
return (source as any)[key];
|
|
84
|
+
},
|
|
85
|
+
set(value) {
|
|
86
|
+
(source as any)[key] = value;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
} catch { }
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const invokeWithFrameworkSpan = (
|
|
94
|
+
handler: Function,
|
|
95
|
+
thisArg: unknown,
|
|
96
|
+
args: any[],
|
|
97
|
+
info: FrameworkSpanInfo,
|
|
98
|
+
options?: SenzorOptions,
|
|
99
|
+
invokeOptions: InvokeOptions = {}
|
|
100
|
+
) => {
|
|
101
|
+
if (!shouldCaptureFrameworkSpan(info.type, options) || !Context.current()) {
|
|
102
|
+
return handler.apply(thisArg, args);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const span = startCapturedSpan(
|
|
106
|
+
info.name,
|
|
107
|
+
'function',
|
|
108
|
+
{
|
|
109
|
+
framework: info.framework,
|
|
110
|
+
'senzor.framework': info.framework,
|
|
111
|
+
'senzor.framework.type': info.type,
|
|
112
|
+
'http.route': info.route,
|
|
113
|
+
route: info.route,
|
|
114
|
+
method: info.method,
|
|
115
|
+
layerPath: info.layerPath,
|
|
116
|
+
handlerName: info.handlerName,
|
|
117
|
+
...info.attributes
|
|
118
|
+
},
|
|
119
|
+
options
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (!span) return handler.apply(thisArg, args);
|
|
123
|
+
|
|
124
|
+
let ended = false;
|
|
125
|
+
const cleanup: Array<() => void> = [];
|
|
126
|
+
|
|
127
|
+
const endSpan = (
|
|
128
|
+
status = statusFrom(info),
|
|
129
|
+
meta: Record<string, unknown> = {}
|
|
130
|
+
) => {
|
|
131
|
+
if (ended) return;
|
|
132
|
+
ended = true;
|
|
133
|
+
|
|
134
|
+
for (const clean of cleanup) {
|
|
135
|
+
try { clean(); } catch { }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
span.end(status, meta);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const res = info.response;
|
|
142
|
+
if (invokeOptions.responseEndsSpan !== false && res?.once) {
|
|
143
|
+
const onFinish = () =>
|
|
144
|
+
endSpan(statusFrom(info), { completion: 'response.finish' });
|
|
145
|
+
const onClose = () =>
|
|
146
|
+
endSpan(statusFrom(info), { completion: 'response.close' });
|
|
147
|
+
|
|
148
|
+
res.once('finish', onFinish);
|
|
149
|
+
res.once('close', onClose);
|
|
150
|
+
|
|
151
|
+
cleanup.push(() => {
|
|
152
|
+
try { res.removeListener?.('finish', onFinish); } catch { }
|
|
153
|
+
try { res.removeListener?.('close', onClose); } catch { }
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const callbackIndex =
|
|
158
|
+
invokeOptions.callbackIndex ??
|
|
159
|
+
args.findIndex((arg) => typeof arg === 'function');
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
invokeOptions.callbackCompletesSpan !== false &&
|
|
163
|
+
callbackIndex >= 0 &&
|
|
164
|
+
typeof args[callbackIndex] === 'function'
|
|
165
|
+
) {
|
|
166
|
+
const originalCallback = args[callbackIndex];
|
|
167
|
+
args[callbackIndex] = function wrappedFrameworkCallback(
|
|
168
|
+
this: unknown,
|
|
169
|
+
...callbackArgs: any[]
|
|
170
|
+
) {
|
|
171
|
+
const maybeError = callbackArgs[0];
|
|
172
|
+
const hasError = !ignoredNextValues.has(maybeError);
|
|
173
|
+
|
|
174
|
+
endSpan(hasError ? 500 : statusFrom(info), {
|
|
175
|
+
completion: 'callback',
|
|
176
|
+
error: hasError ? String(maybeError?.message || maybeError) : undefined,
|
|
177
|
+
'error.type': hasError
|
|
178
|
+
? maybeError?.name || typeof maybeError
|
|
179
|
+
: undefined
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
return runWithParentOf(span, () =>
|
|
183
|
+
originalCallback.apply(this, callbackArgs)
|
|
184
|
+
);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return runWithCapturedSpan(span, () => {
|
|
189
|
+
try {
|
|
190
|
+
const result = handler.apply(thisArg, args);
|
|
191
|
+
|
|
192
|
+
if (isPromiseLike(result)) {
|
|
193
|
+
return result.then(
|
|
194
|
+
(value) => {
|
|
195
|
+
endSpan(statusFrom(info), { completion: 'promise.resolve' });
|
|
196
|
+
return value;
|
|
197
|
+
},
|
|
198
|
+
(error) => {
|
|
199
|
+
endSpan(500, {
|
|
200
|
+
completion: 'promise.reject',
|
|
201
|
+
error: error?.message,
|
|
202
|
+
'error.type': error?.name || 'Error'
|
|
203
|
+
});
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (callbackIndex < 0 && invokeOptions.responseEndsSpan === false) {
|
|
210
|
+
endSpan(statusFrom(info), { completion: 'sync.return' });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return result;
|
|
214
|
+
} catch (error: any) {
|
|
215
|
+
endSpan(500, {
|
|
216
|
+
completion: 'throw',
|
|
217
|
+
error: error?.message,
|
|
218
|
+
'error.type': error?.name || 'Error'
|
|
219
|
+
});
|
|
220
|
+
throw error;
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const wrapFrameworkHandler = <T extends Function>(
|
|
226
|
+
handler: T,
|
|
227
|
+
getInfo: (thisArg: unknown, args: any[]) => FrameworkSpanInfo,
|
|
228
|
+
options?: SenzorOptions,
|
|
229
|
+
invokeOptions: InvokeOptions = {}
|
|
230
|
+
): T => {
|
|
231
|
+
if (typeof handler !== 'function') return handler;
|
|
232
|
+
|
|
233
|
+
const wrapped = function wrappedFrameworkHandler(
|
|
234
|
+
this: unknown,
|
|
235
|
+
...args: any[]
|
|
236
|
+
) {
|
|
237
|
+
return invokeWithFrameworkSpan(
|
|
238
|
+
handler,
|
|
239
|
+
this,
|
|
240
|
+
args,
|
|
241
|
+
getInfo(this, args),
|
|
242
|
+
options,
|
|
243
|
+
invokeOptions
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
copyFunctionProperties(handler, wrapped);
|
|
248
|
+
return wrapped as unknown as T;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export const wrapFrameworkHandlerWithArity = <T extends Function>(
|
|
252
|
+
handler: T,
|
|
253
|
+
getInfo: (thisArg: unknown, args: any[]) => FrameworkSpanInfo,
|
|
254
|
+
options?: SenzorOptions,
|
|
255
|
+
invokeOptions: InvokeOptions = {}
|
|
256
|
+
): T => {
|
|
257
|
+
if (typeof handler !== 'function') return handler;
|
|
258
|
+
|
|
259
|
+
const invoke = (thisArg: unknown, args: any[]) =>
|
|
260
|
+
invokeWithFrameworkSpan(
|
|
261
|
+
handler,
|
|
262
|
+
thisArg,
|
|
263
|
+
args,
|
|
264
|
+
getInfo(thisArg, args),
|
|
265
|
+
options,
|
|
266
|
+
invokeOptions
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
let wrapped: Function;
|
|
270
|
+
|
|
271
|
+
switch (handler.length) {
|
|
272
|
+
case 4:
|
|
273
|
+
wrapped = function wrapped4(this: unknown, a: any, b: any, c: any, d: any) {
|
|
274
|
+
return invoke(this, [a, b, c, d]);
|
|
275
|
+
};
|
|
276
|
+
break;
|
|
277
|
+
case 3:
|
|
278
|
+
wrapped = function wrapped3(this: unknown, a: any, b: any, c: any) {
|
|
279
|
+
return invoke(this, [a, b, c]);
|
|
280
|
+
};
|
|
281
|
+
break;
|
|
282
|
+
case 2:
|
|
283
|
+
wrapped = function wrapped2(this: unknown, a: any, b: any) {
|
|
284
|
+
return invoke(this, [a, b]);
|
|
285
|
+
};
|
|
286
|
+
break;
|
|
287
|
+
case 1:
|
|
288
|
+
wrapped = function wrapped1(this: unknown, a: any) {
|
|
289
|
+
return invoke(this, [a]);
|
|
290
|
+
};
|
|
291
|
+
break;
|
|
292
|
+
default:
|
|
293
|
+
wrapped = function wrapped0(this: unknown) {
|
|
294
|
+
return invoke(this, Array.from(arguments));
|
|
295
|
+
};
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
copyFunctionProperties(handler, wrapped);
|
|
300
|
+
return wrapped as unknown as T;
|
|
301
|
+
};
|