@senzops/apm-node 1.2.0 → 1.2.1
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/CHANGELOG.md +4 -0
- package/README.md +12 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -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/core/client.ts +8 -2
- package/src/core/types.ts +5 -0
- package/src/instrumentation/express.ts +338 -0
- package/src/instrumentation/fastify.ts +296 -0
- package/src/instrumentation/framework.ts +301 -0
- package/src/instrumentation/hook.ts +49 -31
- package/src/instrumentation/koa.ts +173 -0
- package/src/register.ts +16 -0
- package/src/wrappers/fastify.ts +10 -7
- package/src/wrappers/h3.ts +40 -16
- package/src/wrappers/next.ts +68 -21
- package/wiki.md +8 -0
|
@@ -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
|
+
};
|
|
@@ -6,8 +6,8 @@ const SENZOR_PATCHED =
|
|
|
6
6
|
const SENZOR_HOOKS =
|
|
7
7
|
Symbol.for('senzor.require.hooks');
|
|
8
8
|
|
|
9
|
-
type HookFn =
|
|
10
|
-
(exports: unknown) => void;
|
|
9
|
+
type HookFn =
|
|
10
|
+
(exports: unknown) => unknown | void;
|
|
11
11
|
|
|
12
12
|
type HookMap =
|
|
13
13
|
Map<string, HookFn[]>;
|
|
@@ -37,10 +37,10 @@ function getHookRegistry(): HookMap {
|
|
|
37
37
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function runHooks(
|
|
41
|
-
moduleName: string,
|
|
42
|
-
exports: unknown
|
|
43
|
-
) {
|
|
40
|
+
function runHooks(
|
|
41
|
+
moduleName: string,
|
|
42
|
+
exports: unknown
|
|
43
|
+
) {
|
|
44
44
|
|
|
45
45
|
const registry =
|
|
46
46
|
(Module as unknown as Record<
|
|
@@ -48,18 +48,27 @@ function runHooks(
|
|
|
48
48
|
HookMap
|
|
49
49
|
>)[SENZOR_HOOKS];
|
|
50
50
|
|
|
51
|
-
if (!registry) return;
|
|
51
|
+
if (!registry) return exports;
|
|
52
52
|
|
|
53
53
|
const hooks =
|
|
54
54
|
registry.get(moduleName);
|
|
55
55
|
|
|
56
|
-
if (!hooks?.length) return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
if (!hooks?.length) return exports;
|
|
57
|
+
|
|
58
|
+
let currentExports =
|
|
59
|
+
exports;
|
|
60
|
+
|
|
61
|
+
for (const hook of hooks) {
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const nextExports =
|
|
65
|
+
hook(currentExports);
|
|
66
|
+
|
|
67
|
+
if (nextExports !== undefined) {
|
|
68
|
+
currentExports =
|
|
69
|
+
nextExports;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
63
72
|
catch (err) {
|
|
64
73
|
|
|
65
74
|
console.error(
|
|
@@ -69,9 +78,11 @@ function runHooks(
|
|
|
69
78
|
|
|
70
79
|
}
|
|
71
80
|
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return currentExports;
|
|
84
|
+
|
|
85
|
+
}
|
|
75
86
|
|
|
76
87
|
function patchLoaderOnce() {
|
|
77
88
|
|
|
@@ -98,12 +109,13 @@ function patchLoaderOnce() {
|
|
|
98
109
|
arguments
|
|
99
110
|
);
|
|
100
111
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
112
|
+
const patchedExports =
|
|
113
|
+
runHooks(
|
|
114
|
+
request,
|
|
115
|
+
exports
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
return patchedExports;
|
|
107
119
|
|
|
108
120
|
};
|
|
109
121
|
|
|
@@ -137,11 +149,17 @@ function patchCached(
|
|
|
137
149
|
|
|
138
150
|
if (cached?.exports) {
|
|
139
151
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
const replacement =
|
|
153
|
+
hook(
|
|
154
|
+
cached.exports
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (replacement !== undefined) {
|
|
158
|
+
cached.exports =
|
|
159
|
+
replacement;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
}
|
|
145
163
|
|
|
146
164
|
}
|
|
147
165
|
catch { }
|
|
@@ -159,7 +177,7 @@ function tryRequire(
|
|
|
159
177
|
require(moduleName);
|
|
160
178
|
|
|
161
179
|
if (mod) {
|
|
162
|
-
hook(mod);
|
|
180
|
+
hook(mod);
|
|
163
181
|
}
|
|
164
182
|
|
|
165
183
|
}
|
|
@@ -188,7 +206,7 @@ function retryPatch(
|
|
|
188
206
|
|
|
189
207
|
if (mod) {
|
|
190
208
|
|
|
191
|
-
hook(mod);
|
|
209
|
+
hook(mod);
|
|
192
210
|
|
|
193
211
|
clearInterval(timer);
|
|
194
212
|
|
|
@@ -244,4 +262,4 @@ export const hookRequire =
|
|
|
244
262
|
onRequire
|
|
245
263
|
);
|
|
246
264
|
|
|
247
|
-
};
|
|
265
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { normalizePath } from '../core/normalizer';
|
|
2
|
+
import { SenzorOptions } from '../core/types';
|
|
3
|
+
import { hookRequire } from './hook';
|
|
4
|
+
import { patchMethod } from './patch';
|
|
5
|
+
import { wrapFrameworkHandlerWithArity } from './framework';
|
|
6
|
+
|
|
7
|
+
const routerMethods = [
|
|
8
|
+
'all',
|
|
9
|
+
'del',
|
|
10
|
+
'delete',
|
|
11
|
+
'get',
|
|
12
|
+
'head',
|
|
13
|
+
'options',
|
|
14
|
+
'patch',
|
|
15
|
+
'post',
|
|
16
|
+
'put'
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const stringifyPath = (value: unknown): string | undefined => {
|
|
20
|
+
if (typeof value === 'string') return value;
|
|
21
|
+
if (value instanceof RegExp) return value.toString();
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
return value.map(stringifyPath).filter(Boolean).join(',');
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getPathFromArgs = (args: any[]): string | undefined => {
|
|
29
|
+
for (const arg of args) {
|
|
30
|
+
if (typeof arg === 'function') return undefined;
|
|
31
|
+
const path = stringifyPath(arg);
|
|
32
|
+
if (path) return path;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return undefined;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const wrapKoaMiddleware = (
|
|
39
|
+
middleware: any,
|
|
40
|
+
options?: SenzorOptions,
|
|
41
|
+
layerPath?: string,
|
|
42
|
+
layerType: 'middleware' | 'router' | 'route_handler' = 'middleware',
|
|
43
|
+
method?: string
|
|
44
|
+
) => {
|
|
45
|
+
if (typeof middleware !== 'function') return middleware;
|
|
46
|
+
|
|
47
|
+
return wrapFrameworkHandlerWithArity(
|
|
48
|
+
middleware,
|
|
49
|
+
(_thisArg, args) => {
|
|
50
|
+
const ctx = args[0];
|
|
51
|
+
const route =
|
|
52
|
+
ctx?._matchedRoute ||
|
|
53
|
+
ctx?.matched?.[0]?.path ||
|
|
54
|
+
layerPath ||
|
|
55
|
+
normalizePath(ctx?.path || ctx?.request?.path || '/');
|
|
56
|
+
const actualMethod = method || ctx?.method || ctx?.request?.method;
|
|
57
|
+
const handlerName = middleware.name || layerType;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
framework: 'koa',
|
|
61
|
+
type: layerType,
|
|
62
|
+
name:
|
|
63
|
+
layerType === 'route_handler'
|
|
64
|
+
? `koa.request_handler ${actualMethod || ''} ${route}`.trim()
|
|
65
|
+
: `koa.${layerType} ${route || handlerName}`,
|
|
66
|
+
route,
|
|
67
|
+
method: actualMethod,
|
|
68
|
+
layerPath,
|
|
69
|
+
handlerName,
|
|
70
|
+
request: ctx?.req || ctx?.request,
|
|
71
|
+
response: ctx?.res || ctx?.response,
|
|
72
|
+
attributes: {
|
|
73
|
+
'koa.type': layerType,
|
|
74
|
+
'http.route': route,
|
|
75
|
+
path: ctx?.path || ctx?.request?.path
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
options,
|
|
80
|
+
{
|
|
81
|
+
callbackCompletesSpan: false,
|
|
82
|
+
responseEndsSpan: false
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const patchKoaApplication = (
|
|
88
|
+
koa: any,
|
|
89
|
+
options?: SenzorOptions
|
|
90
|
+
) => {
|
|
91
|
+
const proto = koa?.prototype || koa?.default?.prototype;
|
|
92
|
+
if (!proto) return;
|
|
93
|
+
|
|
94
|
+
patchMethod(
|
|
95
|
+
proto,
|
|
96
|
+
'use',
|
|
97
|
+
'senzor.koa.application.use',
|
|
98
|
+
(original) =>
|
|
99
|
+
function patchedKoaUse(this: any, middleware: any) {
|
|
100
|
+
return original.call(
|
|
101
|
+
this,
|
|
102
|
+
wrapKoaMiddleware(middleware, options, undefined, 'middleware')
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const patchKoaRouter = (
|
|
109
|
+
routerModule: any,
|
|
110
|
+
options?: SenzorOptions
|
|
111
|
+
) => {
|
|
112
|
+
const Router =
|
|
113
|
+
routerModule?.Router ||
|
|
114
|
+
routerModule?.default ||
|
|
115
|
+
routerModule;
|
|
116
|
+
const proto = Router?.prototype;
|
|
117
|
+
if (!proto) return;
|
|
118
|
+
|
|
119
|
+
patchMethod(
|
|
120
|
+
proto,
|
|
121
|
+
'use',
|
|
122
|
+
'senzor.koa.router.use',
|
|
123
|
+
(original) =>
|
|
124
|
+
function patchedKoaRouterUse(this: any, ...args: any[]) {
|
|
125
|
+
const layerPath = getPathFromArgs(args);
|
|
126
|
+
const nextArgs = args.map((arg) =>
|
|
127
|
+
typeof arg === 'function'
|
|
128
|
+
? wrapKoaMiddleware(arg, options, layerPath, 'router')
|
|
129
|
+
: arg
|
|
130
|
+
);
|
|
131
|
+
return original.apply(this, nextArgs);
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
for (const method of routerMethods) {
|
|
136
|
+
patchMethod(
|
|
137
|
+
proto,
|
|
138
|
+
method,
|
|
139
|
+
`senzor.koa.router.${method}`,
|
|
140
|
+
(original) =>
|
|
141
|
+
function patchedKoaRouterMethod(this: any, ...args: any[]) {
|
|
142
|
+
const layerPath = getPathFromArgs(args);
|
|
143
|
+
const nextArgs = args.map((arg) =>
|
|
144
|
+
typeof arg === 'function'
|
|
145
|
+
? wrapKoaMiddleware(
|
|
146
|
+
arg,
|
|
147
|
+
options,
|
|
148
|
+
layerPath,
|
|
149
|
+
'route_handler',
|
|
150
|
+
method.toUpperCase()
|
|
151
|
+
)
|
|
152
|
+
: arg
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return original.apply(this, nextArgs);
|
|
156
|
+
}
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
export const instrumentKoa = (options?: SenzorOptions) => {
|
|
162
|
+
hookRequire('koa', (exports: any) => {
|
|
163
|
+
patchKoaApplication(exports, options);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
hookRequire('@koa/router', (exports: any) => {
|
|
167
|
+
patchKoaRouter(exports, options);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
hookRequire('koa-router', (exports: any) => {
|
|
171
|
+
patchKoaRouter(exports, options);
|
|
172
|
+
});
|
|
173
|
+
};
|
package/src/register.ts
CHANGED
|
@@ -31,6 +31,22 @@ const options = {
|
|
|
31
31
|
captureHeaders: truthy(process.env.SENZOR_CAPTURE_HEADERS),
|
|
32
32
|
captureDbStatement:
|
|
33
33
|
process.env.SENZOR_CAPTURE_DB_STATEMENT === 'false'
|
|
34
|
+
? false
|
|
35
|
+
: undefined,
|
|
36
|
+
frameworkSpans:
|
|
37
|
+
process.env.SENZOR_FRAMEWORK_SPANS === 'false'
|
|
38
|
+
? false
|
|
39
|
+
: undefined,
|
|
40
|
+
captureMiddlewareSpans:
|
|
41
|
+
process.env.SENZOR_CAPTURE_MIDDLEWARE_SPANS === 'false'
|
|
42
|
+
? false
|
|
43
|
+
: undefined,
|
|
44
|
+
captureRouterSpans:
|
|
45
|
+
process.env.SENZOR_CAPTURE_ROUTER_SPANS === 'false'
|
|
46
|
+
? false
|
|
47
|
+
: undefined,
|
|
48
|
+
captureLifecycleHookSpans:
|
|
49
|
+
process.env.SENZOR_CAPTURE_LIFECYCLE_HOOK_SPANS === 'false'
|
|
34
50
|
? false
|
|
35
51
|
: undefined
|
|
36
52
|
};
|
package/src/wrappers/fastify.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { client } from '../core/client';
|
|
2
|
-
import { SenzorOptions } from '../core/types';
|
|
3
|
-
import {
|
|
1
|
+
import { client } from '../core/client';
|
|
2
|
+
import { SenzorOptions } from '../core/types';
|
|
3
|
+
import { instrumentFastifyInstance } from '../instrumentation/fastify';
|
|
4
|
+
import { getClientIp } from '../utils/getClientIp';
|
|
4
5
|
|
|
5
6
|
export const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {
|
|
6
|
-
if (options && options.apiKey) {
|
|
7
|
-
client.init(options);
|
|
8
|
-
}
|
|
7
|
+
if (options && options.apiKey) {
|
|
8
|
+
client.init(options);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
instrumentFastifyInstance(fastify, options);
|
|
9
12
|
|
|
10
13
|
fastify.addHook('onRequest', (request: any, reply: any, next: Function) => {
|
|
11
14
|
client.startTrace({
|
|
@@ -29,4 +32,4 @@ export const senzorPlugin = (fastify: any, options: SenzorOptions, done: Functio
|
|
|
29
32
|
});
|
|
30
33
|
|
|
31
34
|
done();
|
|
32
|
-
};
|
|
35
|
+
};
|