@justanalyticsapp/node 0.1.0

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 (52) hide show
  1. package/dist/client.d.ts +286 -0
  2. package/dist/client.js +681 -0
  3. package/dist/client.js.map +1 -0
  4. package/dist/context.d.ts +126 -0
  5. package/dist/context.js +170 -0
  6. package/dist/context.js.map +1 -0
  7. package/dist/errors.d.ts +135 -0
  8. package/dist/errors.js +180 -0
  9. package/dist/errors.js.map +1 -0
  10. package/dist/index.d.ts +301 -0
  11. package/dist/index.js +314 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/integrations/express.d.ts +77 -0
  14. package/dist/integrations/express.js +87 -0
  15. package/dist/integrations/express.js.map +1 -0
  16. package/dist/integrations/http.d.ts +129 -0
  17. package/dist/integrations/http.js +465 -0
  18. package/dist/integrations/http.js.map +1 -0
  19. package/dist/integrations/metrics.d.ts +110 -0
  20. package/dist/integrations/metrics.js +313 -0
  21. package/dist/integrations/metrics.js.map +1 -0
  22. package/dist/integrations/next.d.ts +252 -0
  23. package/dist/integrations/next.js +480 -0
  24. package/dist/integrations/next.js.map +1 -0
  25. package/dist/integrations/pg.d.ts +169 -0
  26. package/dist/integrations/pg.js +616 -0
  27. package/dist/integrations/pg.js.map +1 -0
  28. package/dist/integrations/pino.d.ts +52 -0
  29. package/dist/integrations/pino.js +153 -0
  30. package/dist/integrations/pino.js.map +1 -0
  31. package/dist/integrations/redis.d.ts +190 -0
  32. package/dist/integrations/redis.js +597 -0
  33. package/dist/integrations/redis.js.map +1 -0
  34. package/dist/integrations/winston.d.ts +48 -0
  35. package/dist/integrations/winston.js +99 -0
  36. package/dist/integrations/winston.js.map +1 -0
  37. package/dist/logger.d.ts +148 -0
  38. package/dist/logger.js +162 -0
  39. package/dist/logger.js.map +1 -0
  40. package/dist/span.d.ts +192 -0
  41. package/dist/span.js +197 -0
  42. package/dist/span.js.map +1 -0
  43. package/dist/transport.d.ts +246 -0
  44. package/dist/transport.js +654 -0
  45. package/dist/transport.js.map +1 -0
  46. package/dist/utils/headers.d.ts +60 -0
  47. package/dist/utils/headers.js +93 -0
  48. package/dist/utils/headers.js.map +1 -0
  49. package/dist/utils/id.d.ts +23 -0
  50. package/dist/utils/id.js +36 -0
  51. package/dist/utils/id.js.map +1 -0
  52. package/package.json +65 -0
@@ -0,0 +1,597 @@
1
+ "use strict";
2
+ /**
3
+ * @file packages/node-sdk/src/integrations/redis.ts
4
+ * @description Redis auto-instrumentation integration for the JustAnalytics Node.js SDK.
5
+ *
6
+ * Implements Story 040 - Redis Auto-Instrumentation
7
+ *
8
+ * Monkey-patches `ioredis` (Redis.prototype.sendCommand) and/or `@redis/client`
9
+ * (Commander.prototype.sendCommand) to automatically create `redis.command` spans
10
+ * for all Redis operations. This captures all Redis traffic regardless of which
11
+ * higher-level abstraction the developer uses.
12
+ *
13
+ * Follows the same monkey-patching pattern established in Story 036 (HTTP Auto-Instrumentation)
14
+ * and Story 039 (PostgreSQL Auto-Instrumentation):
15
+ * - Integration class with `enable()`/`disable()` lifecycle methods
16
+ * - Original function preservation for clean teardown
17
+ * - Integration with AsyncLocalStorage context for automatic parent-child span relationships
18
+ * - Fail-open design: if instrumentation code fails, the original command executes normally
19
+ *
20
+ * CRITICAL SECURITY: Redis values are NEVER captured in span attributes.
21
+ * Only command name + key name(s) are recorded in `db.statement`.
22
+ *
23
+ * References:
24
+ * - OpenTelemetry Database Semantic Conventions (db.system, db.statement, etc.)
25
+ * - Story 035 - Node.js SDK Core (Span, context, transport)
26
+ * - Story 036 - HTTP Auto-Instrumentation (integration pattern)
27
+ * - Story 039 - PostgreSQL Auto-Instrumentation (database integration pattern)
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.RedisIntegration = void 0;
31
+ exports.formatRedisStatement = formatRedisStatement;
32
+ exports.sanitizeRedisConnectionString = sanitizeRedisConnectionString;
33
+ const span_1 = require("../span");
34
+ const context_1 = require("../context");
35
+ const id_1 = require("../utils/id");
36
+ // ============================================================================
37
+ // Command Classification Maps
38
+ // ============================================================================
39
+ /**
40
+ * Commands where the first argument is NOT a key (no key extraction needed).
41
+ * These commands take no key argument or their first arg has special meaning.
42
+ */
43
+ const NO_KEY_COMMANDS = new Set([
44
+ 'PING', 'INFO', 'DBSIZE', 'FLUSHDB', 'FLUSHALL', 'TIME', 'QUIT',
45
+ 'AUTH', 'SELECT', 'CONFIG', 'CLIENT', 'CLUSTER', 'DEBUG', 'MULTI',
46
+ 'EXEC', 'DISCARD', 'WATCH', 'UNWATCH', 'SUBSCRIBE', 'UNSUBSCRIBE',
47
+ 'PSUBSCRIBE', 'PUNSUBSCRIBE', 'PUBLISH', 'SLOWLOG', 'COMMAND',
48
+ 'OBJECT', 'SCRIPT', 'MEMORY', 'MODULE', 'WAIT', 'SAVE', 'BGSAVE',
49
+ 'BGREWRITEAOF', 'LASTSAVE', 'SHUTDOWN',
50
+ ]);
51
+ /**
52
+ * Commands where only the key argument(s) should be included
53
+ * (all subsequent args are values and must be stripped).
54
+ * Map of command name -> number of key arguments.
55
+ *
56
+ * Special values:
57
+ * - `-1`: All args are keys (e.g., MGET, DEL with multiple keys)
58
+ * - `-2`: Alternating key/value pairs (e.g., MSET key1 val1 key2 val2) - extract keys only
59
+ * - `0`: Command with no key args
60
+ * - `N > 0`: Fixed number of key/field args to include
61
+ *
62
+ * For commands not in this map, only the first argument (key) is included.
63
+ */
64
+ const KEY_ONLY_COMMANDS = {
65
+ // Single-key commands: include 1 key
66
+ 'GET': 1, 'SET': 1, 'DEL': -1, 'EXISTS': -1, 'EXPIRE': 1,
67
+ 'EXPIREAT': 1, 'TTL': 1, 'PTTL': 1, 'TYPE': 1, 'PERSIST': 1,
68
+ 'INCR': 1, 'INCRBY': 1, 'INCRBYFLOAT': 1, 'DECR': 1, 'DECRBY': 1,
69
+ 'APPEND': 1, 'STRLEN': 1, 'GETRANGE': 1, 'SETRANGE': 1, 'GETSET': 1,
70
+ 'SETNX': 1, 'SETEX': 1, 'PSETEX': 1, 'GETDEL': 1,
71
+ // Multi-key commands: -1 means all args are keys
72
+ 'MGET': -1, 'UNLINK': -1, 'TOUCH': -1,
73
+ 'MSET': -2, // -2 = alternating key/value pairs, extract keys only
74
+ // Hash commands: include key + field name (2 args)
75
+ 'HGET': 2, 'HSET': 2, 'HDEL': 2, 'HEXISTS': 2, 'HINCRBY': 2,
76
+ 'HINCRBYFLOAT': 2, 'HLEN': 1, 'HKEYS': 1, 'HVALS': 1, 'HGETALL': 1,
77
+ 'HMGET': -1, // key + all fields
78
+ 'HMSET': 2, // key + first field only (rest are field/value pairs)
79
+ 'HSETNX': 2,
80
+ // List commands: include key only
81
+ 'LPUSH': 1, 'RPUSH': 1, 'LPOP': 1, 'RPOP': 1, 'LLEN': 1,
82
+ 'LRANGE': 1, 'LINDEX': 1, 'LSET': 1, 'LREM': 1, 'LTRIM': 1,
83
+ 'RPOPLPUSH': 2, 'LMOVE': 2, 'BLPOP': -1, 'BRPOP': -1,
84
+ // Set commands: include key only
85
+ 'SADD': 1, 'SREM': 1, 'SMEMBERS': 1, 'SISMEMBER': 1, 'SCARD': 1,
86
+ 'SPOP': 1, 'SRANDMEMBER': 1, 'SUNION': -1, 'SINTER': -1, 'SDIFF': -1,
87
+ // Sorted set commands: include key only
88
+ 'ZADD': 1, 'ZREM': 1, 'ZSCORE': 1, 'ZRANK': 1, 'ZREVRANK': 1,
89
+ 'ZRANGE': 1, 'ZRANGEBYSCORE': 1, 'ZREVRANGEBYSCORE': 1, 'ZCARD': 1,
90
+ 'ZCOUNT': 1, 'ZINCRBY': 1, 'ZRANGEBYLEX': 1,
91
+ 'ZUNIONSTORE': 1, 'ZINTERSTORE': 1,
92
+ // Stream commands
93
+ 'XADD': 1, 'XREAD': 0, 'XRANGE': 1, 'XREVRANGE': 1, 'XLEN': 1,
94
+ 'XINFO': 0, 'XACK': 1, 'XDEL': 1, 'XTRIM': 1,
95
+ // Key management
96
+ 'RENAME': 2, 'RENAMENX': 2, 'DUMP': 1, 'RESTORE': 1,
97
+ 'SORT': 1, 'SCAN': 0,
98
+ };
99
+ // ============================================================================
100
+ // Statement Formatting Utilities (exported for testing)
101
+ // ============================================================================
102
+ /**
103
+ * Format the db.statement attribute from a Redis command and its arguments.
104
+ *
105
+ * Rules:
106
+ * 1. Command name is always UPPERCASE
107
+ * 2. For NO_KEY_COMMANDS, statement is just the command name
108
+ * 3. For known commands, include only key name(s) per KEY_ONLY_COMMANDS mapping
109
+ * 4. For unknown commands, include only the first argument (assumed to be the key)
110
+ * 5. Values are NEVER included
111
+ * 6. Truncate to maxStatementLength
112
+ *
113
+ * @param command - Redis command name (e.g., "get", "SET", "hget")
114
+ * @param args - Command arguments array
115
+ * @param maxLength - Maximum statement length (default: 512)
116
+ * @returns Formatted statement (e.g., "GET user:123", "HGET cache:data field")
117
+ */
118
+ function formatRedisStatement(command, args, maxLength = 512) {
119
+ try {
120
+ const upperCommand = command.toUpperCase();
121
+ // No-key commands: just the command name
122
+ if (NO_KEY_COMMANDS.has(upperCommand)) {
123
+ return upperCommand;
124
+ }
125
+ if (!args || args.length === 0) {
126
+ return upperCommand;
127
+ }
128
+ const keyCount = KEY_ONLY_COMMANDS[upperCommand];
129
+ let parts;
130
+ if (keyCount === undefined) {
131
+ // Unknown command: include first arg only (assumed key)
132
+ parts = [upperCommand, String(args[0])];
133
+ }
134
+ else if (keyCount === 0) {
135
+ // Command with no key args
136
+ parts = [upperCommand];
137
+ }
138
+ else if (keyCount === -1) {
139
+ // All args are keys (e.g., MGET, DEL with multiple keys)
140
+ parts = [upperCommand, ...args.map(String)];
141
+ }
142
+ else if (keyCount === -2) {
143
+ // Alternating key/value pairs (MSET key1 val1 key2 val2)
144
+ parts = [upperCommand];
145
+ for (let i = 0; i < args.length; i += 2) {
146
+ parts.push(String(args[i]));
147
+ }
148
+ }
149
+ else {
150
+ // Fixed number of key/field args
151
+ parts = [upperCommand, ...args.slice(0, keyCount).map(String)];
152
+ }
153
+ let result = parts.join(' ');
154
+ if (result.length > maxLength) {
155
+ result = result.substring(0, maxLength - 3) + '...';
156
+ }
157
+ return result;
158
+ }
159
+ catch {
160
+ // If formatting fails, return just the command name as a fallback
161
+ return command.toUpperCase();
162
+ }
163
+ }
164
+ function sanitizeRedisConnectionString(hostOrConnStr, port, password, db, user) {
165
+ // If called with only a string that looks like a redis:// URL, sanitize it
166
+ if (port === undefined && hostOrConnStr.startsWith('redis://')) {
167
+ // Replace password in redis://user:password@host:port/db format
168
+ return hostOrConnStr.replace(/^(redis:\/\/)([^:]+):([^@]+)(@)/, '$1$2:***$4').replace(
169
+ // Also handle redis://:password@host format (no user)
170
+ /^(redis:\/\/:)([^@]+)(@)/, '$1***$3');
171
+ }
172
+ // Build from components
173
+ const host = hostOrConnStr;
174
+ const portNum = port;
175
+ const protocol = 'redis://';
176
+ const authPart = user
177
+ ? `${user}:***@`
178
+ : password
179
+ ? `:***@`
180
+ : '';
181
+ const dbPart = db && db !== 0 ? `/${db}` : '';
182
+ return `${protocol}${authPart}${host}:${portNum}${dbPart}`;
183
+ }
184
+ // ============================================================================
185
+ // RedisIntegration Class
186
+ // ============================================================================
187
+ /**
188
+ * RedisIntegration monkey-patches ioredis and/or @redis/client
189
+ * to auto-create redis.command spans for all Redis operations.
190
+ *
191
+ * Follows the same pattern as HttpIntegration (Story 036) and
192
+ * PgIntegration (Story 039):
193
+ * - Constructor accepts serviceName, options, onSpanEnd callback
194
+ * - enable() patches, disable() restores
195
+ * - Original functions preserved for clean teardown
196
+ * - Fail-open design: instrumentation failures never crash the host process
197
+ *
198
+ * Security: NEVER captures Redis values in span attributes.
199
+ * Only command name + key name(s) are recorded.
200
+ */
201
+ class RedisIntegration {
202
+ /**
203
+ * Create a new RedisIntegration.
204
+ *
205
+ * @param serviceName - The service name for span attribution
206
+ * @param options - Integration configuration options
207
+ * @param onSpanEnd - Callback invoked when a span ends (enqueues to transport)
208
+ */
209
+ constructor(serviceName, options, onSpanEnd) {
210
+ this._enabled = false;
211
+ // ioredis originals
212
+ this._originalIoRedisSendCommand = null;
213
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
214
+ this._ioRedisPrototype = null;
215
+ // @redis/client originals
216
+ this._originalNodeRedisSendCommand = null;
217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
+ this._nodeRedisPrototype = null;
219
+ this._serviceName = serviceName;
220
+ this._onSpanEnd = onSpanEnd;
221
+ // Resolve options with defaults
222
+ const opts = options || {};
223
+ this._options = {
224
+ enabled: opts.enabled !== false,
225
+ maxStatementLength: opts.maxStatementLength ?? 512,
226
+ };
227
+ }
228
+ /**
229
+ * Activate the integration: load Redis modules and patch prototypes.
230
+ *
231
+ * Attempts to load both ioredis and @redis/client independently.
232
+ * If neither is installed, silently skips. If only one is installed,
233
+ * only that library is patched.
234
+ *
235
+ * Calling enable() when already enabled is a no-op (idempotent).
236
+ */
237
+ enable() {
238
+ if (this._enabled)
239
+ return;
240
+ if (!this._options.enabled)
241
+ return;
242
+ try {
243
+ let patchedAny = false;
244
+ // Attempt to patch ioredis
245
+ try {
246
+ this._patchIoRedis();
247
+ if (this._ioRedisPrototype) {
248
+ patchedAny = true;
249
+ }
250
+ }
251
+ catch {
252
+ // ioredis not available or patching failed -- silently skip
253
+ }
254
+ // Attempt to patch @redis/client
255
+ try {
256
+ this._patchNodeRedis();
257
+ if (this._nodeRedisPrototype) {
258
+ patchedAny = true;
259
+ }
260
+ }
261
+ catch {
262
+ // @redis/client not available or patching failed -- silently skip
263
+ }
264
+ if (!patchedAny) {
265
+ if (typeof console !== 'undefined') {
266
+ console.debug('[JustAnalytics] Neither ioredis nor @redis/client found. Skipping Redis auto-instrumentation.');
267
+ }
268
+ return;
269
+ }
270
+ this._enabled = true;
271
+ }
272
+ catch (error) {
273
+ // Top-level failure: restore any partial patches
274
+ this._restoreOriginals();
275
+ if (typeof console !== 'undefined') {
276
+ console.warn('[JustAnalytics] Failed to enable Redis integration:', error instanceof Error ? error.message : String(error));
277
+ }
278
+ }
279
+ }
280
+ /**
281
+ * Deactivate: restore original Redis functions.
282
+ *
283
+ * Calling disable() when not enabled is a no-op (idempotent).
284
+ */
285
+ disable() {
286
+ if (!this._enabled)
287
+ return;
288
+ this._restoreOriginals();
289
+ this._enabled = false;
290
+ }
291
+ /**
292
+ * Attempt to load and patch ioredis.
293
+ *
294
+ * ioredis routes ALL commands (including pipeline/cluster) through
295
+ * Redis.prototype.sendCommand. By patching this one method, we capture
296
+ * all commands regardless of which method the developer calls.
297
+ */
298
+ _patchIoRedis() {
299
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
300
+ let ioredis;
301
+ try {
302
+ ioredis = require('ioredis');
303
+ }
304
+ catch {
305
+ // ioredis module is not installed -- silently skip
306
+ if (typeof console !== 'undefined') {
307
+ console.debug('[JustAnalytics] ioredis module not found. Skipping ioredis auto-instrumentation.');
308
+ }
309
+ return;
310
+ }
311
+ const RedisClass = ioredis.default || ioredis;
312
+ if (!RedisClass || !RedisClass.prototype) {
313
+ if (typeof console !== 'undefined') {
314
+ console.debug('[JustAnalytics] ioredis.Redis prototype not found. Skipping ioredis auto-instrumentation.');
315
+ }
316
+ return;
317
+ }
318
+ // Store references for restoration
319
+ this._ioRedisPrototype = RedisClass.prototype;
320
+ this._originalIoRedisSendCommand = RedisClass.prototype.sendCommand;
321
+ // Patch sendCommand
322
+ RedisClass.prototype.sendCommand = this._wrapIoRedisSendCommand(this._originalIoRedisSendCommand);
323
+ }
324
+ /**
325
+ * Attempt to load and patch @redis/client.
326
+ *
327
+ * node-redis v4+ uses a Commander class. All commands go through
328
+ * sendCommand(args: string[]) where args[0] is the command name.
329
+ */
330
+ _patchNodeRedis() {
331
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
332
+ let nodeRedis;
333
+ try {
334
+ nodeRedis = require('@redis/client');
335
+ }
336
+ catch {
337
+ // @redis/client module is not installed -- silently skip
338
+ if (typeof console !== 'undefined') {
339
+ console.debug('[JustAnalytics] @redis/client module not found. Skipping node-redis auto-instrumentation.');
340
+ }
341
+ return;
342
+ }
343
+ // node-redis v4+ exposes a default Commander or client class
344
+ // The exact internal structure varies, so we try multiple paths defensively
345
+ const Commander = nodeRedis.Commander || nodeRedis.RedisClient;
346
+ if (Commander && Commander.prototype && Commander.prototype.sendCommand) {
347
+ this._nodeRedisPrototype = Commander.prototype;
348
+ this._originalNodeRedisSendCommand = Commander.prototype.sendCommand;
349
+ Commander.prototype.sendCommand = this._wrapNodeRedisSendCommand(this._originalNodeRedisSendCommand);
350
+ return;
351
+ }
352
+ // Fallback: try to find sendCommand on the default export
353
+ if (nodeRedis.default && nodeRedis.default.prototype && nodeRedis.default.prototype.sendCommand) {
354
+ this._nodeRedisPrototype = nodeRedis.default.prototype;
355
+ this._originalNodeRedisSendCommand = nodeRedis.default.prototype.sendCommand;
356
+ nodeRedis.default.prototype.sendCommand = this._wrapNodeRedisSendCommand(this._originalNodeRedisSendCommand);
357
+ return;
358
+ }
359
+ if (typeof console !== 'undefined') {
360
+ console.debug('[JustAnalytics] @redis/client sendCommand method not found. Skipping node-redis auto-instrumentation.');
361
+ }
362
+ }
363
+ /**
364
+ * Restore all patched functions to their originals.
365
+ */
366
+ _restoreOriginals() {
367
+ // Restore ioredis
368
+ if (this._ioRedisPrototype && this._originalIoRedisSendCommand) {
369
+ this._ioRedisPrototype.sendCommand = this._originalIoRedisSendCommand;
370
+ this._originalIoRedisSendCommand = null;
371
+ this._ioRedisPrototype = null;
372
+ }
373
+ // Restore @redis/client
374
+ if (this._nodeRedisPrototype && this._originalNodeRedisSendCommand) {
375
+ this._nodeRedisPrototype.sendCommand = this._originalNodeRedisSendCommand;
376
+ this._originalNodeRedisSendCommand = null;
377
+ this._nodeRedisPrototype = null;
378
+ }
379
+ }
380
+ /**
381
+ * Wrap ioredis Redis.prototype.sendCommand to create redis.command spans.
382
+ *
383
+ * ioredis routes ALL commands (including pipeline/cluster) through
384
+ * sendCommand(command: Command). The Command object has:
385
+ * - command.name: string (e.g., "get", "set", "hget")
386
+ * - command.args: unknown[] (command arguments)
387
+ * - command.promise: Promise (resolves/rejects when the command completes)
388
+ *
389
+ * @param original - The original sendCommand function
390
+ * @returns A wrapped version
391
+ */
392
+ _wrapIoRedisSendCommand(original) {
393
+ const integration = this;
394
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
395
+ return function patchedSendCommand(...args) {
396
+ try {
397
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
+ const command = args[0];
399
+ if (!command || !command.name) {
400
+ return original.apply(this, args);
401
+ }
402
+ const commandName = command.name.toUpperCase();
403
+ const commandArgs = command.args || [];
404
+ // Get active span context
405
+ const parentSpan = (0, context_1.getActiveSpan)();
406
+ const traceId = parentSpan?.traceId ?? (0, id_1.generateTraceId)();
407
+ const parentSpanId = parentSpan?.id ?? null;
408
+ const statement = formatRedisStatement(commandName, commandArgs, integration._options.maxStatementLength);
409
+ const operationName = `redis.command ${commandName}`;
410
+ // Build attributes (OpenTelemetry Database Semantic Conventions)
411
+ const attributes = {
412
+ 'db.system': 'redis',
413
+ 'db.statement': statement,
414
+ 'db.operation': commandName,
415
+ };
416
+ // Extract connection info from ioredis client instance
417
+ const connInfo = integration._getIoRedisConnectionInfo(this);
418
+ Object.assign(attributes, connInfo);
419
+ // Create span
420
+ const span = new span_1.Span({
421
+ operationName,
422
+ serviceName: integration._serviceName,
423
+ kind: 'client',
424
+ traceId,
425
+ parentSpanId,
426
+ attributes,
427
+ });
428
+ // Call original sendCommand
429
+ const result = original.apply(this, args);
430
+ // ioredis Command has a promise property
431
+ if (command.promise && typeof command.promise.then === 'function') {
432
+ command.promise.then(() => {
433
+ span.end();
434
+ integration._onSpanEnd(span);
435
+ }, (error) => {
436
+ span.setStatus('error', error instanceof Error ? error.message : String(error));
437
+ span.end();
438
+ integration._onSpanEnd(span);
439
+ });
440
+ }
441
+ else {
442
+ // Fallback: end span immediately
443
+ span.end();
444
+ integration._onSpanEnd(span);
445
+ }
446
+ return result;
447
+ }
448
+ catch (error) {
449
+ // If instrumentation code fails, fall through to original
450
+ return original.apply(this, args);
451
+ }
452
+ };
453
+ }
454
+ /**
455
+ * Wrap @redis/client's sendCommand to create redis.command spans.
456
+ *
457
+ * node-redis v4+ uses a Commander class that dispatches all commands
458
+ * through a sendCommand method. The method signature is:
459
+ * sendCommand(args: string[], options?: CommandOptions): Promise<unknown>
460
+ *
461
+ * Where args[0] is the command name and args[1..] are command arguments.
462
+ *
463
+ * @param original - The original sendCommand function
464
+ * @returns A wrapped version
465
+ */
466
+ _wrapNodeRedisSendCommand(original) {
467
+ const integration = this;
468
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
469
+ return function patchedSendCommand(...args) {
470
+ try {
471
+ const commandArgs = args[0];
472
+ if (!Array.isArray(commandArgs) || commandArgs.length === 0) {
473
+ return original.apply(this, args);
474
+ }
475
+ const commandName = commandArgs[0].toUpperCase();
476
+ const redisArgs = commandArgs.slice(1);
477
+ // Get active span context
478
+ const parentSpan = (0, context_1.getActiveSpan)();
479
+ const traceId = parentSpan?.traceId ?? (0, id_1.generateTraceId)();
480
+ const parentSpanId = parentSpan?.id ?? null;
481
+ const statement = formatRedisStatement(commandName, redisArgs, integration._options.maxStatementLength);
482
+ const operationName = `redis.command ${commandName}`;
483
+ // Build attributes (OpenTelemetry Database Semantic Conventions)
484
+ const attributes = {
485
+ 'db.system': 'redis',
486
+ 'db.statement': statement,
487
+ 'db.operation': commandName,
488
+ };
489
+ // Extract connection info from @redis/client instance
490
+ const connInfo = integration._getNodeRedisConnectionInfo(this);
491
+ Object.assign(attributes, connInfo);
492
+ // Create span
493
+ const span = new span_1.Span({
494
+ operationName,
495
+ serviceName: integration._serviceName,
496
+ kind: 'client',
497
+ traceId,
498
+ parentSpanId,
499
+ attributes,
500
+ });
501
+ // Call original -- node-redis sendCommand returns a Promise
502
+ try {
503
+ const result = original.apply(this, args);
504
+ if (result && typeof result.then === 'function') {
505
+ return result.then((value) => {
506
+ span.end();
507
+ integration._onSpanEnd(span);
508
+ return value;
509
+ }, (error) => {
510
+ span.setStatus('error', error instanceof Error ? error.message : String(error));
511
+ span.end();
512
+ integration._onSpanEnd(span);
513
+ throw error;
514
+ });
515
+ }
516
+ // Sync return (unlikely)
517
+ span.end();
518
+ integration._onSpanEnd(span);
519
+ return result;
520
+ }
521
+ catch (error) {
522
+ span.setStatus('error', error instanceof Error ? error.message : String(error));
523
+ span.end();
524
+ integration._onSpanEnd(span);
525
+ throw error;
526
+ }
527
+ }
528
+ catch (error) {
529
+ // If instrumentation code fails, fall through to original
530
+ return original.apply(this, args);
531
+ }
532
+ };
533
+ }
534
+ /**
535
+ * Extract connection info from an ioredis client instance.
536
+ *
537
+ * ioredis stores connection options on the instance as `this.options`.
538
+ *
539
+ * @param client - The ioredis client instance (`this` in the patched method)
540
+ * @returns Attributes object with connection info
541
+ */
542
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
543
+ _getIoRedisConnectionInfo(client) {
544
+ const attrs = {};
545
+ try {
546
+ const options = client.options || {};
547
+ const host = options.host || 'localhost';
548
+ const port = options.port || 6379;
549
+ const db = options.db || 0;
550
+ attrs['net.peer.name'] = host;
551
+ attrs['net.peer.port'] = port;
552
+ if (db && db !== 0) {
553
+ attrs['db.redis.database_index'] = db;
554
+ }
555
+ // Build sanitized connection string
556
+ attrs['db.connection_string'] = sanitizeRedisConnectionString(host, port, options.password, db, options.username);
557
+ }
558
+ catch {
559
+ // If connection info extraction fails, return what we have
560
+ }
561
+ return attrs;
562
+ }
563
+ /**
564
+ * Extract connection info from a @redis/client instance.
565
+ *
566
+ * node-redis v4+ stores connection options in various ways depending
567
+ * on the version. We try multiple paths defensively.
568
+ *
569
+ * @param client - The @redis/client instance (`this` in the patched method)
570
+ * @returns Attributes object with connection info
571
+ */
572
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
573
+ _getNodeRedisConnectionInfo(client) {
574
+ const attrs = {};
575
+ try {
576
+ // node-redis v4 stores options in various places
577
+ const options = client.options || client._options || {};
578
+ const socket = options.socket || {};
579
+ const host = socket.host || options.host || 'localhost';
580
+ const port = socket.port || options.port || 6379;
581
+ const db = options.database || 0;
582
+ attrs['net.peer.name'] = host;
583
+ attrs['net.peer.port'] = port;
584
+ if (db && db !== 0) {
585
+ attrs['db.redis.database_index'] = db;
586
+ }
587
+ // Build sanitized connection string
588
+ attrs['db.connection_string'] = sanitizeRedisConnectionString(host, port, options.password, db, options.username);
589
+ }
590
+ catch {
591
+ // If connection info extraction fails, return what we have
592
+ }
593
+ return attrs;
594
+ }
595
+ }
596
+ exports.RedisIntegration = RedisIntegration;
597
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/integrations/redis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @file packages/node-sdk/src/integrations/winston.ts
3
+ * @description Winston transport for JustAnalytics log integration.
4
+ *
5
+ * Implements Story 046 - SDK Log Integration
6
+ *
7
+ * Pipes Winston log entries through the JustAnalytics SDK logger,
8
+ * automatically attaching trace context and batching for transport.
9
+ *
10
+ * Usage:
11
+ * ```typescript
12
+ * import winston from 'winston';
13
+ * import { JustAnalyticsWinstonTransport } from '@justanalyticsapp/node/winston';
14
+ *
15
+ * const logger = winston.createLogger({
16
+ * transports: [
17
+ * new winston.transports.Console(),
18
+ * new JustAnalyticsWinstonTransport(),
19
+ * ],
20
+ * });
21
+ * ```
22
+ *
23
+ * The transport requires `winston` and `winston-transport` as peer dependencies.
24
+ * Install them separately: `npm install winston winston-transport`
25
+ */
26
+ import type { LogLevel } from '../logger';
27
+ /** Configuration options for the JustAnalytics Winston transport */
28
+ export interface JustAnalyticsWinstonTransportOptions {
29
+ /** Custom mapping from Winston levels to JA levels */
30
+ levelMap?: Record<string, LogLevel>;
31
+ /** Standard Winston transport options (level, silent, etc.) */
32
+ level?: string;
33
+ silent?: boolean;
34
+ handleExceptions?: boolean;
35
+ handleRejections?: boolean;
36
+ }
37
+ /**
38
+ * Winston transport that forwards log entries to JustAnalytics.
39
+ *
40
+ * Extracts message, level, and metadata from Winston info objects
41
+ * and pipes them through JA.logger for batched transport with
42
+ * automatic trace context attachment.
43
+ */
44
+ export declare class JustAnalyticsWinstonTransport {
45
+ private readonly levelMap;
46
+ constructor(opts?: JustAnalyticsWinstonTransportOptions);
47
+ log(info: any, callback: () => void): void;
48
+ }