@senzops/apm-node 1.2.3 → 1.2.5
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/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 +172 -167
- package/src/core/context.ts +2 -2
- package/src/core/types.ts +48 -43
- package/src/instrumentation/express.ts +53 -20
- package/src/instrumentation/http.ts +1 -1
- package/src/middleware/express.ts +2 -2
- package/src/wrappers/fastify.ts +13 -13
- package/src/wrappers/h3.ts +41 -40
- package/src/wrappers/next.ts +70 -68
|
@@ -57,18 +57,24 @@ const getRequestRoute = (
|
|
|
57
57
|
layer: any,
|
|
58
58
|
layerPath?: string
|
|
59
59
|
): string | undefined => {
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (layer?.route?.path) {
|
|
61
|
+
const routePath = stringifyPath(layer.route.path);
|
|
62
62
|
const baseUrl = req?.baseUrl || '';
|
|
63
|
+
// If baseUrl already ends with or contains the routePath, be careful
|
|
63
64
|
return `${baseUrl}${routePath}` || routePath;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
if (req?.route?.path) {
|
|
67
|
-
|
|
68
|
+
const baseUrl = req?.baseUrl || '';
|
|
69
|
+
return `${baseUrl}${req.route.path}`;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
if (layerPath) {
|
|
71
73
|
const baseUrl = req?.baseUrl || '';
|
|
74
|
+
// If baseUrl already contains layerPath, don't double it
|
|
75
|
+
if (baseUrl === layerPath || baseUrl.endsWith(layerPath)) {
|
|
76
|
+
return baseUrl;
|
|
77
|
+
}
|
|
72
78
|
return `${baseUrl}${layerPath}` || layerPath;
|
|
73
79
|
}
|
|
74
80
|
|
|
@@ -84,9 +90,14 @@ const getLayerType = (
|
|
|
84
90
|
if (forcedType) return forcedType;
|
|
85
91
|
if (original.length === 4) return 'error_handler' as const;
|
|
86
92
|
if (layer?.route) return 'request_handler' as const;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
93
|
+
|
|
94
|
+
const isRouter =
|
|
95
|
+
layer?.name === 'router' ||
|
|
96
|
+
layer?.handle?.name === 'router' ||
|
|
97
|
+
typeof layer?.handle?.stack !== 'undefined' ||
|
|
98
|
+
typeof layer?.handle?.route === 'function';
|
|
99
|
+
|
|
100
|
+
if (isRouter) return 'router' as const;
|
|
90
101
|
return 'middleware' as const;
|
|
91
102
|
};
|
|
92
103
|
|
|
@@ -105,6 +116,7 @@ const copyEnumerableProperties = (
|
|
|
105
116
|
source: Function,
|
|
106
117
|
target: Function
|
|
107
118
|
) => {
|
|
119
|
+
// Copy enumerable properties
|
|
108
120
|
for (const key in source as any) {
|
|
109
121
|
try {
|
|
110
122
|
Object.defineProperty(target, key, {
|
|
@@ -119,6 +131,13 @@ const copyEnumerableProperties = (
|
|
|
119
131
|
});
|
|
120
132
|
} catch { }
|
|
121
133
|
}
|
|
134
|
+
|
|
135
|
+
// Ensure 'stack' is copied even if not enumerable (though it usually is)
|
|
136
|
+
if ((source as any).stack && !(target as any).stack) {
|
|
137
|
+
try {
|
|
138
|
+
(target as any).stack = (source as any).stack;
|
|
139
|
+
} catch { }
|
|
140
|
+
}
|
|
122
141
|
};
|
|
123
142
|
|
|
124
143
|
const patchLayer = (
|
|
@@ -145,7 +164,7 @@ const patchLayer = (
|
|
|
145
164
|
const handlerName =
|
|
146
165
|
original.name ||
|
|
147
166
|
layer.name ||
|
|
148
|
-
layerType;
|
|
167
|
+
(layerType === 'request_handler' ? 'handler' : layerType);
|
|
149
168
|
|
|
150
169
|
if (original.length === 4) {
|
|
151
170
|
const wrapped = function senzorExpressErrorHandler(
|
|
@@ -264,7 +283,7 @@ const patchRouteMethodHandlers = (
|
|
|
264
283
|
const stack = this?.stack || [];
|
|
265
284
|
|
|
266
285
|
for (const layer of stack) {
|
|
267
|
-
|
|
286
|
+
patchLayer(layer, routePath, options, 'request_handler');
|
|
268
287
|
}
|
|
269
288
|
|
|
270
289
|
return result;
|
|
@@ -277,10 +296,13 @@ const getSafeRouter = (app: any) => {
|
|
|
277
296
|
if (!app) return undefined;
|
|
278
297
|
if (app._router) return app._router;
|
|
279
298
|
try {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
299
|
+
// In Express 4, app.router is a getter that throws.
|
|
300
|
+
// We only want it if it's not a throwing getter (Express 3)
|
|
301
|
+
// or if it actually has a stack.
|
|
302
|
+
const r = app.router;
|
|
303
|
+
if (r && (r.stack || typeof r === 'function')) return r;
|
|
304
|
+
} catch { }
|
|
305
|
+
return undefined;
|
|
284
306
|
};
|
|
285
307
|
|
|
286
308
|
const patchExpress = (
|
|
@@ -290,7 +312,7 @@ const patchExpress = (
|
|
|
290
312
|
if (!expressModule) return;
|
|
291
313
|
|
|
292
314
|
const routerProto =
|
|
293
|
-
typeof expressModule?.Router?.prototype?.
|
|
315
|
+
typeof expressModule?.Router?.prototype?.use === 'function'
|
|
294
316
|
? expressModule.Router.prototype
|
|
295
317
|
: expressModule.Router;
|
|
296
318
|
|
|
@@ -302,8 +324,9 @@ const patchExpress = (
|
|
|
302
324
|
function patchedExpressRoute(this: any, ...args: any[]) {
|
|
303
325
|
const route = original.apply(this, args);
|
|
304
326
|
const routePath = getLayerPath(args);
|
|
305
|
-
const
|
|
327
|
+
const stack = this?.stack || [];
|
|
306
328
|
|
|
329
|
+
const layer = stack[stack.length - 1];
|
|
307
330
|
patchLayer(layer, routePath, options, 'router');
|
|
308
331
|
patchRouteMethodHandlers(route, routePath, options);
|
|
309
332
|
|
|
@@ -317,9 +340,13 @@ const patchExpress = (
|
|
|
317
340
|
'senzor.express.router.use',
|
|
318
341
|
(original) =>
|
|
319
342
|
function patchedExpressRouterUse(this: any, ...args: any[]) {
|
|
343
|
+
const prevStackLength = this?.stack?.length || 0;
|
|
320
344
|
const result = original.apply(this, args);
|
|
321
|
-
const
|
|
322
|
-
|
|
345
|
+
const stack = this?.stack || [];
|
|
346
|
+
|
|
347
|
+
for (let i = prevStackLength; i < stack.length; i++) {
|
|
348
|
+
patchLayer(stack[i], getLayerPath(args), options);
|
|
349
|
+
}
|
|
323
350
|
return result;
|
|
324
351
|
}
|
|
325
352
|
);
|
|
@@ -330,11 +357,17 @@ const patchExpress = (
|
|
|
330
357
|
'senzor.express.application.use',
|
|
331
358
|
(original) =>
|
|
332
359
|
function patchedExpressApplicationUse(this: any, ...args: any[]) {
|
|
333
|
-
const
|
|
360
|
+
const routerBefore = getSafeRouter(this);
|
|
361
|
+
const prevStackLength = routerBefore?.stack?.length || 0;
|
|
362
|
+
|
|
334
363
|
const result = original.apply(this, args);
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
364
|
+
|
|
365
|
+
const routerAfter = getSafeRouter(this);
|
|
366
|
+
const stack = routerAfter?.stack || [];
|
|
367
|
+
|
|
368
|
+
for (let i = prevStackLength; i < stack.length; i++) {
|
|
369
|
+
patchLayer(stack[i], getLayerPath(args), options);
|
|
370
|
+
}
|
|
338
371
|
return result;
|
|
339
372
|
}
|
|
340
373
|
);
|
|
@@ -3,7 +3,7 @@ import { getClientIp } from '../utils/getClientIp';
|
|
|
3
3
|
|
|
4
4
|
// 1. Request Handler (Place before routes)
|
|
5
5
|
export const expressMiddleware = () => {
|
|
6
|
-
return (req: any, res: any, next: () => void)
|
|
6
|
+
return function senzorMiddleware(req: any, res: any, next: () => void) {
|
|
7
7
|
client.startTrace({
|
|
8
8
|
method: req.method,
|
|
9
9
|
path: req.originalUrl || req.url,
|
|
@@ -37,7 +37,7 @@ export const expressMiddleware = () => {
|
|
|
37
37
|
// 2. Error Handler (Place after routes)
|
|
38
38
|
// This is required in Express to capture the actual Error Object (Stack Trace)
|
|
39
39
|
export const expressErrorHandler = () => {
|
|
40
|
-
return (err: any, req: any, res: any, next: (err?: any) => void)
|
|
40
|
+
return function senzorErrorHandler(err: any, req: any, res: any, next: (err?: any) => void) {
|
|
41
41
|
|
|
42
42
|
// 1. Capture the exception context
|
|
43
43
|
client.captureError(err);
|
package/src/wrappers/fastify.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { client } from '../core/client';
|
|
2
|
-
import { SenzorOptions } from '../core/types';
|
|
3
|
-
import { instrumentFastifyInstance } from '../instrumentation/fastify';
|
|
4
|
-
import { getClientIp } from '../utils/getClientIp';
|
|
1
|
+
import { client } from '../core/client';
|
|
2
|
+
import { SenzorOptions } from '../core/types';
|
|
3
|
+
import { instrumentFastifyInstance } from '../instrumentation/fastify';
|
|
4
|
+
import { getClientIp } from '../utils/getClientIp';
|
|
5
5
|
|
|
6
6
|
export const senzorPlugin = (fastify: any, options: SenzorOptions, done: Function) => {
|
|
7
|
-
if (options && options.apiKey) {
|
|
8
|
-
client.init(options);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
instrumentFastifyInstance(fastify, options);
|
|
7
|
+
if (options && options.apiKey) {
|
|
8
|
+
client.init(options);
|
|
9
|
+
}
|
|
12
10
|
|
|
13
|
-
fastify
|
|
11
|
+
instrumentFastifyInstance(fastify, options);
|
|
12
|
+
|
|
13
|
+
fastify.addHook('onRequest', function senzorOnRequest(request: any, reply: any, next: Function) {
|
|
14
14
|
client.startTrace({
|
|
15
15
|
method: request.method,
|
|
16
16
|
path: request.raw.url || request.url,
|
|
@@ -20,16 +20,16 @@ export const senzorPlugin = (fastify: any, options: SenzorOptions, done: Functio
|
|
|
20
20
|
}, () => next());
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
fastify.addHook('onError', (request: any, reply: any, error: any, next: Function)
|
|
23
|
+
fastify.addHook('onError', function senzorOnError(request: any, reply: any, error: any, next: Function) {
|
|
24
24
|
client.captureError(error);
|
|
25
25
|
next();
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
-
fastify.addHook('onResponse', (request: any, reply: any, next: Function)
|
|
28
|
+
fastify.addHook('onResponse', function senzorOnResponse(request: any, reply: any, next: Function) {
|
|
29
29
|
const route = request.routeOptions?.url || request.routerPath || 'UNKNOWN';
|
|
30
30
|
client.endTrace(reply.statusCode, { route });
|
|
31
31
|
next();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
done();
|
|
35
|
-
};
|
|
35
|
+
};
|
package/src/wrappers/h3.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { client } from '../core/client';
|
|
2
|
-
import { getRoute } from '../core/normalizer';
|
|
3
|
-
import { invokeWithFrameworkSpan } from '../instrumentation/framework';
|
|
4
|
-
import { getClientIp } from '../utils/getClientIp';
|
|
1
|
+
import { client } from '../core/client';
|
|
2
|
+
import { getRoute } from '../core/normalizer';
|
|
3
|
+
import { invokeWithFrameworkSpan } from '../instrumentation/framework';
|
|
4
|
+
import { getClientIp } from '../utils/getClientIp';
|
|
5
5
|
|
|
6
6
|
type EventHandler = (event: any) => any;
|
|
7
7
|
|
|
@@ -16,44 +16,45 @@ export const wrapH3 = (handler: EventHandler) => {
|
|
|
16
16
|
ip: getClientIp(req),
|
|
17
17
|
userAgent: req.headers['user-agent'],
|
|
18
18
|
headers: req.headers // Pass headers
|
|
19
|
-
}, async () => {
|
|
20
|
-
try {
|
|
21
|
-
const route = getRoute(event, path);
|
|
22
|
-
const response = await invokeWithFrameworkSpan(
|
|
23
|
-
handler,
|
|
24
|
-
undefined,
|
|
25
|
-
[event],
|
|
26
|
-
{
|
|
27
|
-
framework: 'h3',
|
|
28
|
-
type: 'event_handler',
|
|
29
|
-
name: `h3.event_handler ${req.method || 'GET'} ${route}`,
|
|
30
|
-
route,
|
|
31
|
-
method: req.method || 'GET',
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
19
|
+
}, async () => {
|
|
20
|
+
try {
|
|
21
|
+
const route = getRoute(event, path);
|
|
22
|
+
const response = await invokeWithFrameworkSpan(
|
|
23
|
+
handler,
|
|
24
|
+
undefined,
|
|
25
|
+
[event],
|
|
26
|
+
{
|
|
27
|
+
framework: 'h3',
|
|
28
|
+
type: 'event_handler',
|
|
29
|
+
name: `h3.event_handler ${req.method || 'GET'} ${route}`,
|
|
30
|
+
route,
|
|
31
|
+
method: req.method || 'GET',
|
|
32
|
+
handlerName: (handler as any).name || 'handler',
|
|
33
|
+
request: req,
|
|
34
|
+
response: event.node.res,
|
|
35
|
+
attributes: {
|
|
36
|
+
'h3.type': 'event_handler',
|
|
37
|
+
'http.route': route
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
undefined,
|
|
41
|
+
{
|
|
42
|
+
callbackCompletesSpan: false,
|
|
43
|
+
responseEndsSpan: false
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
let status = 200;
|
|
47
|
+
if (event.node.res.statusCode) status = event.node.res.statusCode;
|
|
48
|
+
if (response && response.statusCode) status = response.statusCode;
|
|
49
|
+
|
|
50
|
+
client.endTrace(status, { route });
|
|
51
|
+
return response;
|
|
52
|
+
} catch (err: any) {
|
|
53
|
+
client.captureError(err);
|
|
54
|
+
const status = err.statusCode || err.status || 500;
|
|
54
55
|
client.endTrace(status, { route: getRoute(event, path) });
|
|
55
56
|
throw err;
|
|
56
57
|
}
|
|
57
58
|
});
|
|
58
59
|
};
|
|
59
|
-
};
|
|
60
|
+
};
|
package/src/wrappers/next.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { client } from '../core/client';
|
|
2
|
-
import { normalizePath } from '../core/normalizer';
|
|
3
|
-
import { invokeWithFrameworkSpan } from '../instrumentation/framework';
|
|
4
|
-
import { getClientIp } from '../utils/getClientIp';
|
|
1
|
+
import { client } from '../core/client';
|
|
2
|
+
import { normalizePath } from '../core/normalizer';
|
|
3
|
+
import { invokeWithFrameworkSpan } from '../instrumentation/framework';
|
|
4
|
+
import { getClientIp } from '../utils/getClientIp';
|
|
5
5
|
|
|
6
6
|
// --- App Router Wrapper ---
|
|
7
7
|
export const wrapNextRoute = (handler: Function) => {
|
|
@@ -38,39 +38,40 @@ export const wrapNextRoute = (handler: Function) => {
|
|
|
38
38
|
userAgent: ua,
|
|
39
39
|
ip: ip || getClientIp(req),
|
|
40
40
|
headers: headers // Pass extracted headers
|
|
41
|
-
}, async () => {
|
|
42
|
-
try {
|
|
43
|
-
const route = normalizePath(url.pathname);
|
|
44
|
-
const response = await invokeWithFrameworkSpan(
|
|
45
|
-
handler,
|
|
46
|
-
undefined,
|
|
47
|
-
[req, context],
|
|
48
|
-
{
|
|
49
|
-
framework: 'next',
|
|
50
|
-
type: 'route_handler',
|
|
51
|
-
name: `next.app_route_handler ${method} ${route}`,
|
|
52
|
-
route,
|
|
53
|
-
method,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
'
|
|
58
|
-
'
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
client.
|
|
41
|
+
}, async () => {
|
|
42
|
+
try {
|
|
43
|
+
const route = normalizePath(url.pathname);
|
|
44
|
+
const response = await invokeWithFrameworkSpan(
|
|
45
|
+
handler,
|
|
46
|
+
undefined,
|
|
47
|
+
[req, context],
|
|
48
|
+
{
|
|
49
|
+
framework: 'next',
|
|
50
|
+
type: 'route_handler',
|
|
51
|
+
name: `next.app_route_handler ${method} ${route}`,
|
|
52
|
+
route,
|
|
53
|
+
method,
|
|
54
|
+
handlerName: (handler as any).name || 'handler',
|
|
55
|
+
request: req,
|
|
56
|
+
attributes: {
|
|
57
|
+
'next.router': 'app',
|
|
58
|
+
'http.route': route,
|
|
59
|
+
'url.path': url.pathname
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
undefined,
|
|
63
|
+
{
|
|
64
|
+
callbackCompletesSpan: false,
|
|
65
|
+
responseEndsSpan: false
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
const status = response?.status || 200;
|
|
69
|
+
|
|
70
|
+
client.endTrace(status, { route });
|
|
71
|
+
return response;
|
|
72
|
+
} catch (err: any) {
|
|
73
|
+
client.captureError(err);
|
|
74
|
+
client.endTrace(500, { route: normalizePath(url.pathname) });
|
|
74
75
|
throw err;
|
|
75
76
|
}
|
|
76
77
|
});
|
|
@@ -95,37 +96,38 @@ export const wrapNextPages = (handler: Function) => {
|
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
res.once('finish', done);
|
|
98
|
-
res.once('close', done);
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
const route = normalizePath(path);
|
|
102
|
-
return await invokeWithFrameworkSpan(
|
|
103
|
-
handler,
|
|
104
|
-
undefined,
|
|
105
|
-
[req, res],
|
|
106
|
-
{
|
|
107
|
-
framework: 'next',
|
|
108
|
-
type: 'route_handler',
|
|
109
|
-
name: `next.pages_api_handler ${req.method || 'GET'} ${route}`,
|
|
110
|
-
route,
|
|
111
|
-
method: req.method || 'GET',
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
99
|
+
res.once('close', done);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const route = normalizePath(path);
|
|
103
|
+
return await invokeWithFrameworkSpan(
|
|
104
|
+
handler,
|
|
105
|
+
undefined,
|
|
106
|
+
[req, res],
|
|
107
|
+
{
|
|
108
|
+
framework: 'next',
|
|
109
|
+
type: 'route_handler',
|
|
110
|
+
name: `next.pages_api_handler ${req.method || 'GET'} ${route}`,
|
|
111
|
+
route,
|
|
112
|
+
method: req.method || 'GET',
|
|
113
|
+
handlerName: (handler as any).name || 'handler',
|
|
114
|
+
request: req,
|
|
115
|
+
response: res,
|
|
116
|
+
attributes: {
|
|
117
|
+
'next.router': 'pages',
|
|
118
|
+
'http.route': route
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
undefined,
|
|
122
|
+
{
|
|
123
|
+
callbackCompletesSpan: false,
|
|
124
|
+
responseEndsSpan: true
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
} catch (e: any) {
|
|
128
|
+
client.captureError(e);
|
|
129
|
+
throw e;
|
|
128
130
|
}
|
|
129
131
|
});
|
|
130
132
|
};
|
|
131
|
-
};
|
|
133
|
+
};
|