@senzops/apm-node 1.2.8 → 1.3.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +527 -398
  3. package/dist/index.d.mts +5 -0
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.global.js +1 -1
  6. package/dist/index.global.js.map +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +1 -1
  10. package/dist/index.mjs.map +1 -1
  11. package/dist/lambda-handler.d.mts +13 -0
  12. package/dist/lambda-handler.d.ts +13 -0
  13. package/dist/lambda-handler.js +2 -0
  14. package/dist/lambda-handler.js.map +1 -0
  15. package/dist/lambda-handler.mjs +2 -0
  16. package/dist/lambda-handler.mjs.map +1 -0
  17. package/dist/register.js +1 -1
  18. package/dist/register.js.map +1 -1
  19. package/dist/register.mjs +1 -1
  20. package/dist/register.mjs.map +1 -1
  21. package/package.json +6 -1
  22. package/src/core/client.ts +57 -0
  23. package/src/core/transport.ts +20 -3
  24. package/src/core/types.ts +5 -1
  25. package/src/index.ts +4 -0
  26. package/src/instrumentation/amqplib.ts +371 -0
  27. package/src/instrumentation/anthropic.ts +245 -0
  28. package/src/instrumentation/aws-sdk.ts +403 -0
  29. package/src/instrumentation/azure-openai.ts +177 -0
  30. package/src/instrumentation/bunyan.ts +93 -0
  31. package/src/instrumentation/cassandra.ts +367 -0
  32. package/src/instrumentation/cohere.ts +227 -0
  33. package/src/instrumentation/connect.ts +200 -0
  34. package/src/instrumentation/dataloader.ts +291 -0
  35. package/src/instrumentation/dns.ts +220 -0
  36. package/src/instrumentation/firebase.ts +445 -0
  37. package/src/instrumentation/fs.ts +260 -0
  38. package/src/instrumentation/generic-pool.ts +317 -0
  39. package/src/instrumentation/google-genai.ts +426 -0
  40. package/src/instrumentation/graphql.ts +434 -0
  41. package/src/instrumentation/grpc.ts +666 -0
  42. package/src/instrumentation/hapi.ts +257 -0
  43. package/src/instrumentation/kafka.ts +360 -0
  44. package/src/instrumentation/knex.ts +249 -0
  45. package/src/instrumentation/lru-memoizer.ts +175 -0
  46. package/src/instrumentation/memcached.ts +190 -0
  47. package/src/instrumentation/mistral.ts +254 -0
  48. package/src/instrumentation/nestjs.ts +243 -0
  49. package/src/instrumentation/net.ts +171 -0
  50. package/src/instrumentation/openai.ts +281 -0
  51. package/src/instrumentation/pino.ts +170 -0
  52. package/src/instrumentation/restify.ts +213 -0
  53. package/src/instrumentation/runtime.ts +352 -0
  54. package/src/instrumentation/socketio.ts +272 -0
  55. package/src/instrumentation/tedious.ts +509 -0
  56. package/src/instrumentation/winston.ts +149 -0
  57. package/src/lambda-handler.ts +262 -0
  58. package/src/register.ts +22 -3
  59. package/src/wrappers/lambda.ts +417 -0
  60. package/tsup.config.ts +4 -4
  61. package/wiki.md +1693 -852
@@ -0,0 +1,317 @@
1
+ import { SenzorOptions } from '../core/types';
2
+ import { hookRequire } from './hook';
3
+ import { patchMethod } from './patch';
4
+ import { runWithCapturedSpan, startCapturedSpan } from './span';
5
+
6
+ // ---------------------------------------------------------------------------
7
+ // generic-pool Instrumentation
8
+ //
9
+ // Instruments the `generic-pool` package — the de-facto connection/resource
10
+ // pooling library used by many database drivers (pg, mysql2, tedious),
11
+ // cache clients, and custom resource managers in Node.js.
12
+ //
13
+ // Patches Pool.prototype methods:
14
+ // - acquire() — resource acquisition from the pool
15
+ // - release() — resource return to the pool
16
+ // - destroy() — resource destruction
17
+ // - drain() — pool draining (graceful shutdown)
18
+ //
19
+ // These spans measure pool health and contention:
20
+ // - How long acquire() takes reveals pool exhaustion
21
+ // - Release/destroy patterns show resource lifecycle
22
+ //
23
+ // Captured attributes:
24
+ // - pool.type: 'generic-pool'
25
+ // - pool.operation: acquire, release, destroy, drain
26
+ // - pool.size: current pool size
27
+ // - pool.available: available resources
28
+ // - pool.pending: pending acquisition requests
29
+ // - pool.max: maximum pool size
30
+ // - pool.min: minimum pool size
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /** Extract pool stats from a generic-pool Pool instance. */
34
+ const getPoolStats = (pool: any): Record<string, any> => {
35
+ const stats: Record<string, any> = {
36
+ 'pool.type': 'generic-pool',
37
+ };
38
+
39
+ try {
40
+ if (typeof pool.size !== 'undefined') stats['pool.size'] = pool.size;
41
+ if (typeof pool.available !== 'undefined') stats['pool.available'] = pool.available;
42
+ if (typeof pool.pending !== 'undefined') stats['pool.pending'] = pool.pending;
43
+ if (typeof pool.borrowed !== 'undefined') stats['pool.borrowed'] = pool.borrowed;
44
+ if (pool.max !== undefined) stats['pool.max'] = pool.max;
45
+ if (pool.min !== undefined) stats['pool.min'] = pool.min;
46
+
47
+ // Try _config for older versions
48
+ if (pool._config) {
49
+ if (stats['pool.max'] === undefined && pool._config.max) stats['pool.max'] = pool._config.max;
50
+ if (stats['pool.min'] === undefined && pool._config.min) stats['pool.min'] = pool._config.min;
51
+ }
52
+ } catch { }
53
+
54
+ return stats;
55
+ };
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Pool method patching
59
+ // ---------------------------------------------------------------------------
60
+
61
+ const patchPool = (poolProto: any, options?: SenzorOptions) => {
62
+ if (!poolProto) return;
63
+
64
+ // --- acquire(priority?) → Promise<resource> ---
65
+ patchMethod(
66
+ poolProto,
67
+ 'acquire',
68
+ 'senzor.generic-pool.pool.acquire',
69
+ (original) =>
70
+ function patchedAcquire(this: any, ...args: any[]) {
71
+ const poolStats = getPoolStats(this);
72
+
73
+ const span = startCapturedSpan(
74
+ 'Pool acquire',
75
+ 'custom',
76
+ {
77
+ ...poolStats,
78
+ 'pool.operation': 'acquire',
79
+ library: 'generic-pool',
80
+ },
81
+ options
82
+ );
83
+
84
+ if (!span) return original.apply(this, args);
85
+
86
+ return runWithCapturedSpan(span, () => {
87
+ try {
88
+ const result = original.apply(this, args);
89
+
90
+ if (result && typeof result.then === 'function') {
91
+ return result.then(
92
+ (resource: any) => {
93
+ // Capture pool state after acquisition
94
+ const postStats = getPoolStats(this);
95
+ span.end(0, {
96
+ 'pool.size_after': postStats['pool.size'],
97
+ 'pool.available_after': postStats['pool.available'],
98
+ 'pool.pending_after': postStats['pool.pending'],
99
+ });
100
+ return resource;
101
+ },
102
+ (error: any) => {
103
+ span.end(500, {
104
+ 'error.message': error?.message,
105
+ 'error.type': error?.name || 'PoolError',
106
+ });
107
+ throw error;
108
+ }
109
+ );
110
+ }
111
+
112
+ span.end(0);
113
+ return result;
114
+ } catch (error: any) {
115
+ span.end(500, {
116
+ 'error.message': error?.message,
117
+ 'error.type': error?.name || 'Error',
118
+ });
119
+ throw error;
120
+ }
121
+ });
122
+ }
123
+ );
124
+
125
+ // --- release(resource) → Promise<void> ---
126
+ patchMethod(
127
+ poolProto,
128
+ 'release',
129
+ 'senzor.generic-pool.pool.release',
130
+ (original) =>
131
+ function patchedRelease(this: any, resource: any) {
132
+ const poolStats = getPoolStats(this);
133
+
134
+ const span = startCapturedSpan(
135
+ 'Pool release',
136
+ 'custom',
137
+ {
138
+ ...poolStats,
139
+ 'pool.operation': 'release',
140
+ library: 'generic-pool',
141
+ },
142
+ options
143
+ );
144
+
145
+ if (!span) return original.call(this, resource);
146
+
147
+ return runWithCapturedSpan(span, () => {
148
+ try {
149
+ const result = original.call(this, resource);
150
+
151
+ if (result && typeof result.then === 'function') {
152
+ return result.then(
153
+ (value: any) => { span.end(0); return value; },
154
+ (error: any) => {
155
+ span.end(500, { 'error.message': error?.message });
156
+ throw error;
157
+ }
158
+ );
159
+ }
160
+
161
+ span.end(0);
162
+ return result;
163
+ } catch (error: any) {
164
+ span.end(500, { 'error.message': error?.message });
165
+ throw error;
166
+ }
167
+ });
168
+ }
169
+ );
170
+
171
+ // --- destroy(resource) → Promise<void> ---
172
+ patchMethod(
173
+ poolProto,
174
+ 'destroy',
175
+ 'senzor.generic-pool.pool.destroy',
176
+ (original) =>
177
+ function patchedDestroy(this: any, resource: any) {
178
+ const poolStats = getPoolStats(this);
179
+
180
+ const span = startCapturedSpan(
181
+ 'Pool destroy',
182
+ 'custom',
183
+ {
184
+ ...poolStats,
185
+ 'pool.operation': 'destroy',
186
+ library: 'generic-pool',
187
+ },
188
+ options
189
+ );
190
+
191
+ if (!span) return original.call(this, resource);
192
+
193
+ return runWithCapturedSpan(span, () => {
194
+ try {
195
+ const result = original.call(this, resource);
196
+
197
+ if (result && typeof result.then === 'function') {
198
+ return result.then(
199
+ (value: any) => { span.end(0); return value; },
200
+ (error: any) => {
201
+ span.end(500, { 'error.message': error?.message });
202
+ throw error;
203
+ }
204
+ );
205
+ }
206
+
207
+ span.end(0);
208
+ return result;
209
+ } catch (error: any) {
210
+ span.end(500, { 'error.message': error?.message });
211
+ throw error;
212
+ }
213
+ });
214
+ }
215
+ );
216
+
217
+ // --- drain() → Promise<void> ---
218
+ if (typeof poolProto.drain === 'function') {
219
+ patchMethod(
220
+ poolProto,
221
+ 'drain',
222
+ 'senzor.generic-pool.pool.drain',
223
+ (original) =>
224
+ function patchedDrain(this: any) {
225
+ const poolStats = getPoolStats(this);
226
+
227
+ const span = startCapturedSpan(
228
+ 'Pool drain',
229
+ 'custom',
230
+ {
231
+ ...poolStats,
232
+ 'pool.operation': 'drain',
233
+ library: 'generic-pool',
234
+ },
235
+ options
236
+ );
237
+
238
+ if (!span) return original.call(this);
239
+
240
+ return runWithCapturedSpan(span, () => {
241
+ try {
242
+ const result = original.call(this);
243
+
244
+ if (result && typeof result.then === 'function') {
245
+ return result.then(
246
+ (value: any) => { span.end(0); return value; },
247
+ (error: any) => {
248
+ span.end(500, { 'error.message': error?.message });
249
+ throw error;
250
+ }
251
+ );
252
+ }
253
+
254
+ span.end(0);
255
+ return result;
256
+ } catch (error: any) {
257
+ span.end(500, { 'error.message': error?.message });
258
+ throw error;
259
+ }
260
+ });
261
+ }
262
+ );
263
+ }
264
+ };
265
+
266
+ // ---------------------------------------------------------------------------
267
+ // createPool factory wrapping
268
+ // ---------------------------------------------------------------------------
269
+
270
+ const patchCreatePool = (genericPool: any, options?: SenzorOptions) => {
271
+ if (typeof genericPool?.createPool !== 'function') return;
272
+
273
+ patchMethod(
274
+ genericPool,
275
+ 'createPool',
276
+ 'senzor.generic-pool.createPool',
277
+ (original) =>
278
+ function patchedCreatePool(this: any, factory: any, config: any) {
279
+ const pool = original.call(this, factory, config);
280
+
281
+ if (pool && !pool.__senzorPatched) {
282
+ const proto = Object.getPrototypeOf(pool);
283
+ if (proto && !proto.__senzorPatched) {
284
+ patchPool(proto, options);
285
+ proto.__senzorPatched = true;
286
+ }
287
+ pool.__senzorPatched = true;
288
+ }
289
+
290
+ return pool;
291
+ }
292
+ );
293
+ };
294
+
295
+ // ---------------------------------------------------------------------------
296
+ // Public API
297
+ // ---------------------------------------------------------------------------
298
+
299
+ export const instrumentGenericPool = (options?: SenzorOptions) => {
300
+ hookRequire('generic-pool', (exports: any) => {
301
+ // Patch the createPool factory
302
+ patchCreatePool(exports, options);
303
+
304
+ // Also try to find the Pool prototype directly
305
+ if (exports?.Pool?.prototype) {
306
+ patchPool(exports.Pool.prototype, options);
307
+ }
308
+
309
+ // Handle default export
310
+ if (exports?.default) {
311
+ patchCreatePool(exports.default, options);
312
+ if (exports.default.Pool?.prototype) {
313
+ patchPool(exports.default.Pool.prototype, options);
314
+ }
315
+ }
316
+ });
317
+ };