@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.
- package/dist/client.d.ts +286 -0
- package/dist/client.js +681 -0
- package/dist/client.js.map +1 -0
- package/dist/context.d.ts +126 -0
- package/dist/context.js +170 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +135 -0
- package/dist/errors.js +180 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +301 -0
- package/dist/index.js +314 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/express.d.ts +77 -0
- package/dist/integrations/express.js +87 -0
- package/dist/integrations/express.js.map +1 -0
- package/dist/integrations/http.d.ts +129 -0
- package/dist/integrations/http.js +465 -0
- package/dist/integrations/http.js.map +1 -0
- package/dist/integrations/metrics.d.ts +110 -0
- package/dist/integrations/metrics.js +313 -0
- package/dist/integrations/metrics.js.map +1 -0
- package/dist/integrations/next.d.ts +252 -0
- package/dist/integrations/next.js +480 -0
- package/dist/integrations/next.js.map +1 -0
- package/dist/integrations/pg.d.ts +169 -0
- package/dist/integrations/pg.js +616 -0
- package/dist/integrations/pg.js.map +1 -0
- package/dist/integrations/pino.d.ts +52 -0
- package/dist/integrations/pino.js +153 -0
- package/dist/integrations/pino.js.map +1 -0
- package/dist/integrations/redis.d.ts +190 -0
- package/dist/integrations/redis.js +597 -0
- package/dist/integrations/redis.js.map +1 -0
- package/dist/integrations/winston.d.ts +48 -0
- package/dist/integrations/winston.js +99 -0
- package/dist/integrations/winston.js.map +1 -0
- package/dist/logger.d.ts +148 -0
- package/dist/logger.js +162 -0
- package/dist/logger.js.map +1 -0
- package/dist/span.d.ts +192 -0
- package/dist/span.js +197 -0
- package/dist/span.js.map +1 -0
- package/dist/transport.d.ts +246 -0
- package/dist/transport.js +654 -0
- package/dist/transport.js.map +1 -0
- package/dist/utils/headers.d.ts +60 -0
- package/dist/utils/headers.js +93 -0
- package/dist/utils/headers.js.map +1 -0
- package/dist/utils/id.d.ts +23 -0
- package/dist/utils/id.js +36 -0
- package/dist/utils/id.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/node-sdk/src/integrations/pino.ts
|
|
3
|
+
* @description Pino transport for JustAnalytics log integration.
|
|
4
|
+
*
|
|
5
|
+
* Implements Story 046 - SDK Log Integration
|
|
6
|
+
*
|
|
7
|
+
* Creates a writable stream that parses Pino's newline-delimited JSON
|
|
8
|
+
* output and pipes log entries through the JustAnalytics SDK logger.
|
|
9
|
+
*
|
|
10
|
+
* Usage (programmatic):
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import pino from 'pino';
|
|
13
|
+
* import { createJustAnalyticsPinoTransport } from '@justanalyticsapp/node/pino';
|
|
14
|
+
*
|
|
15
|
+
* const logger = pino(
|
|
16
|
+
* { level: 'info' },
|
|
17
|
+
* createJustAnalyticsPinoTransport(),
|
|
18
|
+
* );
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Usage (Pino transport API):
|
|
22
|
+
* ```typescript
|
|
23
|
+
* import pino from 'pino';
|
|
24
|
+
*
|
|
25
|
+
* const logger = pino({
|
|
26
|
+
* transport: {
|
|
27
|
+
* target: '@justanalyticsapp/node/pino',
|
|
28
|
+
* },
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* The transport requires `pino` as a peer dependency.
|
|
33
|
+
*/
|
|
34
|
+
import { Writable } from 'stream';
|
|
35
|
+
import type { LogLevel } from '../logger';
|
|
36
|
+
/** Configuration options for the Pino transport */
|
|
37
|
+
export interface JustAnalyticsPinoTransportOptions {
|
|
38
|
+
/** Custom mapping from Pino numeric levels to JA levels */
|
|
39
|
+
levelMap?: Record<number, LogLevel>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a writable stream that pipes Pino log entries to JustAnalytics.
|
|
43
|
+
*
|
|
44
|
+
* The stream parses newline-delimited JSON from Pino, maps numeric levels
|
|
45
|
+
* to JA levels, strips Pino internal fields, and forwards log entries
|
|
46
|
+
* through JA.logger for batched transport with automatic trace context.
|
|
47
|
+
*
|
|
48
|
+
* @param options - Optional configuration for level mapping
|
|
49
|
+
* @returns A Writable stream compatible with Pino's destination API
|
|
50
|
+
*/
|
|
51
|
+
export declare function createJustAnalyticsPinoTransport(options?: JustAnalyticsPinoTransportOptions): Writable;
|
|
52
|
+
export default createJustAnalyticsPinoTransport;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @file packages/node-sdk/src/integrations/pino.ts
|
|
4
|
+
* @description Pino transport for JustAnalytics log integration.
|
|
5
|
+
*
|
|
6
|
+
* Implements Story 046 - SDK Log Integration
|
|
7
|
+
*
|
|
8
|
+
* Creates a writable stream that parses Pino's newline-delimited JSON
|
|
9
|
+
* output and pipes log entries through the JustAnalytics SDK logger.
|
|
10
|
+
*
|
|
11
|
+
* Usage (programmatic):
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import pino from 'pino';
|
|
14
|
+
* import { createJustAnalyticsPinoTransport } from '@justanalyticsapp/node/pino';
|
|
15
|
+
*
|
|
16
|
+
* const logger = pino(
|
|
17
|
+
* { level: 'info' },
|
|
18
|
+
* createJustAnalyticsPinoTransport(),
|
|
19
|
+
* );
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Usage (Pino transport API):
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import pino from 'pino';
|
|
25
|
+
*
|
|
26
|
+
* const logger = pino({
|
|
27
|
+
* transport: {
|
|
28
|
+
* target: '@justanalyticsapp/node/pino',
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* The transport requires `pino` as a peer dependency.
|
|
34
|
+
*/
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createJustAnalyticsPinoTransport = createJustAnalyticsPinoTransport;
|
|
37
|
+
const stream_1 = require("stream");
|
|
38
|
+
/** Pino numeric level to JustAnalytics level mapping */
|
|
39
|
+
const DEFAULT_PINO_LEVEL_MAP = {
|
|
40
|
+
10: 'debug', // pino 'trace'
|
|
41
|
+
20: 'debug', // pino 'debug'
|
|
42
|
+
30: 'info', // pino 'info'
|
|
43
|
+
40: 'warn', // pino 'warn'
|
|
44
|
+
50: 'error', // pino 'error'
|
|
45
|
+
60: 'fatal', // pino 'fatal'
|
|
46
|
+
};
|
|
47
|
+
/** Pino internal fields to exclude from attributes */
|
|
48
|
+
const PINO_INTERNAL_FIELDS = new Set(['level', 'msg', 'time', 'pid', 'hostname', 'v']);
|
|
49
|
+
/**
|
|
50
|
+
* Map a Pino numeric level to a JA LogLevel.
|
|
51
|
+
* Uses the exact level if found, otherwise finds the closest lower level.
|
|
52
|
+
* Defaults to 'info' if no match is found.
|
|
53
|
+
*
|
|
54
|
+
* @param pinoLevel - Pino numeric log level
|
|
55
|
+
* @param levelMap - Level mapping to use
|
|
56
|
+
* @returns The corresponding JA LogLevel
|
|
57
|
+
*/
|
|
58
|
+
function mapPinoLevel(pinoLevel, levelMap) {
|
|
59
|
+
if (levelMap[pinoLevel])
|
|
60
|
+
return levelMap[pinoLevel];
|
|
61
|
+
// Find closest lower level
|
|
62
|
+
const levels = Object.keys(levelMap)
|
|
63
|
+
.map(Number)
|
|
64
|
+
.sort((a, b) => b - a);
|
|
65
|
+
for (const l of levels) {
|
|
66
|
+
if (l <= pinoLevel)
|
|
67
|
+
return levelMap[l];
|
|
68
|
+
}
|
|
69
|
+
return 'info';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a writable stream that pipes Pino log entries to JustAnalytics.
|
|
73
|
+
*
|
|
74
|
+
* The stream parses newline-delimited JSON from Pino, maps numeric levels
|
|
75
|
+
* to JA levels, strips Pino internal fields, and forwards log entries
|
|
76
|
+
* through JA.logger for batched transport with automatic trace context.
|
|
77
|
+
*
|
|
78
|
+
* @param options - Optional configuration for level mapping
|
|
79
|
+
* @returns A Writable stream compatible with Pino's destination API
|
|
80
|
+
*/
|
|
81
|
+
function createJustAnalyticsPinoTransport(options) {
|
|
82
|
+
const levelMap = options?.levelMap
|
|
83
|
+
? { ...DEFAULT_PINO_LEVEL_MAP, ...options.levelMap }
|
|
84
|
+
: { ...DEFAULT_PINO_LEVEL_MAP };
|
|
85
|
+
let buffer = '';
|
|
86
|
+
return new stream_1.Writable({
|
|
87
|
+
write(chunk, _encoding, callback) {
|
|
88
|
+
try {
|
|
89
|
+
buffer += chunk.toString();
|
|
90
|
+
const lines = buffer.split('\n');
|
|
91
|
+
// Keep the last partial line in the buffer
|
|
92
|
+
buffer = lines.pop() || '';
|
|
93
|
+
for (const line of lines) {
|
|
94
|
+
if (!line.trim())
|
|
95
|
+
continue;
|
|
96
|
+
try {
|
|
97
|
+
const obj = JSON.parse(line);
|
|
98
|
+
const pinoLevel = typeof obj.level === 'number' ? obj.level : 30;
|
|
99
|
+
const mappedLevel = mapPinoLevel(pinoLevel, levelMap);
|
|
100
|
+
const message = obj.msg || '';
|
|
101
|
+
// Extract attributes (everything except Pino internal fields)
|
|
102
|
+
const attributes = {};
|
|
103
|
+
for (const key of Object.keys(obj)) {
|
|
104
|
+
if (!PINO_INTERNAL_FIELDS.has(key)) {
|
|
105
|
+
attributes[key] = obj[key];
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Lazy import to avoid circular dependency
|
|
109
|
+
const JA = require('../index').default;
|
|
110
|
+
if (JA && JA.logger) {
|
|
111
|
+
JA.logger.log(mappedLevel, message, attributes);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Skip malformed JSON lines
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// Never throw from transport
|
|
121
|
+
}
|
|
122
|
+
callback();
|
|
123
|
+
},
|
|
124
|
+
final(callback) {
|
|
125
|
+
// Process any remaining data in the buffer
|
|
126
|
+
if (buffer.trim()) {
|
|
127
|
+
try {
|
|
128
|
+
const obj = JSON.parse(buffer);
|
|
129
|
+
const pinoLevel = typeof obj.level === 'number' ? obj.level : 30;
|
|
130
|
+
const mappedLevel = mapPinoLevel(pinoLevel, levelMap);
|
|
131
|
+
const message = obj.msg || '';
|
|
132
|
+
const attributes = {};
|
|
133
|
+
for (const key of Object.keys(obj)) {
|
|
134
|
+
if (!PINO_INTERNAL_FIELDS.has(key)) {
|
|
135
|
+
attributes[key] = obj[key];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
const JA = require('../index').default;
|
|
139
|
+
if (JA && JA.logger) {
|
|
140
|
+
JA.logger.log(mappedLevel, message, attributes);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Skip malformed data
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
callback();
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Default export for Pino transport API: pino({ transport: { target: '...' } })
|
|
152
|
+
exports.default = createJustAnalyticsPinoTransport;
|
|
153
|
+
//# sourceMappingURL=pino.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pino.js","sourceRoot":"","sources":["../../src/integrations/pino.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file packages/node-sdk/src/integrations/redis.ts
|
|
3
|
+
* @description Redis auto-instrumentation integration for the JustAnalytics Node.js SDK.
|
|
4
|
+
*
|
|
5
|
+
* Implements Story 040 - Redis Auto-Instrumentation
|
|
6
|
+
*
|
|
7
|
+
* Monkey-patches `ioredis` (Redis.prototype.sendCommand) and/or `@redis/client`
|
|
8
|
+
* (Commander.prototype.sendCommand) to automatically create `redis.command` spans
|
|
9
|
+
* for all Redis operations. This captures all Redis traffic regardless of which
|
|
10
|
+
* higher-level abstraction the developer uses.
|
|
11
|
+
*
|
|
12
|
+
* Follows the same monkey-patching pattern established in Story 036 (HTTP Auto-Instrumentation)
|
|
13
|
+
* and Story 039 (PostgreSQL Auto-Instrumentation):
|
|
14
|
+
* - Integration class with `enable()`/`disable()` lifecycle methods
|
|
15
|
+
* - Original function preservation for clean teardown
|
|
16
|
+
* - Integration with AsyncLocalStorage context for automatic parent-child span relationships
|
|
17
|
+
* - Fail-open design: if instrumentation code fails, the original command executes normally
|
|
18
|
+
*
|
|
19
|
+
* CRITICAL SECURITY: Redis values are NEVER captured in span attributes.
|
|
20
|
+
* Only command name + key name(s) are recorded in `db.statement`.
|
|
21
|
+
*
|
|
22
|
+
* References:
|
|
23
|
+
* - OpenTelemetry Database Semantic Conventions (db.system, db.statement, etc.)
|
|
24
|
+
* - Story 035 - Node.js SDK Core (Span, context, transport)
|
|
25
|
+
* - Story 036 - HTTP Auto-Instrumentation (integration pattern)
|
|
26
|
+
* - Story 039 - PostgreSQL Auto-Instrumentation (database integration pattern)
|
|
27
|
+
*/
|
|
28
|
+
import { Span } from '../span';
|
|
29
|
+
/**
|
|
30
|
+
* Configuration for the Redis auto-instrumentation integration.
|
|
31
|
+
*/
|
|
32
|
+
export interface RedisIntegrationOptions {
|
|
33
|
+
/** Enable/disable the integration (default: true) */
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Maximum character length for the db.statement attribute.
|
|
37
|
+
* Statements longer than this are truncated with '...' suffix.
|
|
38
|
+
* (default: 512)
|
|
39
|
+
*/
|
|
40
|
+
maxStatementLength?: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Format the db.statement attribute from a Redis command and its arguments.
|
|
44
|
+
*
|
|
45
|
+
* Rules:
|
|
46
|
+
* 1. Command name is always UPPERCASE
|
|
47
|
+
* 2. For NO_KEY_COMMANDS, statement is just the command name
|
|
48
|
+
* 3. For known commands, include only key name(s) per KEY_ONLY_COMMANDS mapping
|
|
49
|
+
* 4. For unknown commands, include only the first argument (assumed to be the key)
|
|
50
|
+
* 5. Values are NEVER included
|
|
51
|
+
* 6. Truncate to maxStatementLength
|
|
52
|
+
*
|
|
53
|
+
* @param command - Redis command name (e.g., "get", "SET", "hget")
|
|
54
|
+
* @param args - Command arguments array
|
|
55
|
+
* @param maxLength - Maximum statement length (default: 512)
|
|
56
|
+
* @returns Formatted statement (e.g., "GET user:123", "HGET cache:data field")
|
|
57
|
+
*/
|
|
58
|
+
export declare function formatRedisStatement(command: string, args: unknown[], maxLength?: number): string;
|
|
59
|
+
/**
|
|
60
|
+
* Sanitize a Redis connection string by masking the password.
|
|
61
|
+
*
|
|
62
|
+
* Also handles raw redis:// URLs by replacing the password portion.
|
|
63
|
+
*
|
|
64
|
+
* @param connStr - Raw connection string potentially containing a password
|
|
65
|
+
* @returns Sanitized connection string with password masked
|
|
66
|
+
*/
|
|
67
|
+
export declare function sanitizeRedisConnectionString(connStr: string): string;
|
|
68
|
+
/**
|
|
69
|
+
* Build a sanitized Redis connection string from individual components.
|
|
70
|
+
*
|
|
71
|
+
* @param host - Redis host
|
|
72
|
+
* @param port - Redis port
|
|
73
|
+
* @param password - Redis password (will be masked)
|
|
74
|
+
* @param db - Redis database index
|
|
75
|
+
* @param user - Redis user (for ACL auth)
|
|
76
|
+
* @returns Sanitized connection string (e.g., "redis://user:***@host:6379/0")
|
|
77
|
+
*/
|
|
78
|
+
export declare function sanitizeRedisConnectionString(host: string, port: number, password?: string, db?: number, user?: string): string;
|
|
79
|
+
/**
|
|
80
|
+
* RedisIntegration monkey-patches ioredis and/or @redis/client
|
|
81
|
+
* to auto-create redis.command spans for all Redis operations.
|
|
82
|
+
*
|
|
83
|
+
* Follows the same pattern as HttpIntegration (Story 036) and
|
|
84
|
+
* PgIntegration (Story 039):
|
|
85
|
+
* - Constructor accepts serviceName, options, onSpanEnd callback
|
|
86
|
+
* - enable() patches, disable() restores
|
|
87
|
+
* - Original functions preserved for clean teardown
|
|
88
|
+
* - Fail-open design: instrumentation failures never crash the host process
|
|
89
|
+
*
|
|
90
|
+
* Security: NEVER captures Redis values in span attributes.
|
|
91
|
+
* Only command name + key name(s) are recorded.
|
|
92
|
+
*/
|
|
93
|
+
export declare class RedisIntegration {
|
|
94
|
+
private _enabled;
|
|
95
|
+
private _options;
|
|
96
|
+
private _serviceName;
|
|
97
|
+
private _onSpanEnd;
|
|
98
|
+
private _originalIoRedisSendCommand;
|
|
99
|
+
private _ioRedisPrototype;
|
|
100
|
+
private _originalNodeRedisSendCommand;
|
|
101
|
+
private _nodeRedisPrototype;
|
|
102
|
+
/**
|
|
103
|
+
* Create a new RedisIntegration.
|
|
104
|
+
*
|
|
105
|
+
* @param serviceName - The service name for span attribution
|
|
106
|
+
* @param options - Integration configuration options
|
|
107
|
+
* @param onSpanEnd - Callback invoked when a span ends (enqueues to transport)
|
|
108
|
+
*/
|
|
109
|
+
constructor(serviceName: string, options: RedisIntegrationOptions | undefined, onSpanEnd: (span: Span) => void);
|
|
110
|
+
/**
|
|
111
|
+
* Activate the integration: load Redis modules and patch prototypes.
|
|
112
|
+
*
|
|
113
|
+
* Attempts to load both ioredis and @redis/client independently.
|
|
114
|
+
* If neither is installed, silently skips. If only one is installed,
|
|
115
|
+
* only that library is patched.
|
|
116
|
+
*
|
|
117
|
+
* Calling enable() when already enabled is a no-op (idempotent).
|
|
118
|
+
*/
|
|
119
|
+
enable(): void;
|
|
120
|
+
/**
|
|
121
|
+
* Deactivate: restore original Redis functions.
|
|
122
|
+
*
|
|
123
|
+
* Calling disable() when not enabled is a no-op (idempotent).
|
|
124
|
+
*/
|
|
125
|
+
disable(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Attempt to load and patch ioredis.
|
|
128
|
+
*
|
|
129
|
+
* ioredis routes ALL commands (including pipeline/cluster) through
|
|
130
|
+
* Redis.prototype.sendCommand. By patching this one method, we capture
|
|
131
|
+
* all commands regardless of which method the developer calls.
|
|
132
|
+
*/
|
|
133
|
+
private _patchIoRedis;
|
|
134
|
+
/**
|
|
135
|
+
* Attempt to load and patch @redis/client.
|
|
136
|
+
*
|
|
137
|
+
* node-redis v4+ uses a Commander class. All commands go through
|
|
138
|
+
* sendCommand(args: string[]) where args[0] is the command name.
|
|
139
|
+
*/
|
|
140
|
+
private _patchNodeRedis;
|
|
141
|
+
/**
|
|
142
|
+
* Restore all patched functions to their originals.
|
|
143
|
+
*/
|
|
144
|
+
private _restoreOriginals;
|
|
145
|
+
/**
|
|
146
|
+
* Wrap ioredis Redis.prototype.sendCommand to create redis.command spans.
|
|
147
|
+
*
|
|
148
|
+
* ioredis routes ALL commands (including pipeline/cluster) through
|
|
149
|
+
* sendCommand(command: Command). The Command object has:
|
|
150
|
+
* - command.name: string (e.g., "get", "set", "hget")
|
|
151
|
+
* - command.args: unknown[] (command arguments)
|
|
152
|
+
* - command.promise: Promise (resolves/rejects when the command completes)
|
|
153
|
+
*
|
|
154
|
+
* @param original - The original sendCommand function
|
|
155
|
+
* @returns A wrapped version
|
|
156
|
+
*/
|
|
157
|
+
private _wrapIoRedisSendCommand;
|
|
158
|
+
/**
|
|
159
|
+
* Wrap @redis/client's sendCommand to create redis.command spans.
|
|
160
|
+
*
|
|
161
|
+
* node-redis v4+ uses a Commander class that dispatches all commands
|
|
162
|
+
* through a sendCommand method. The method signature is:
|
|
163
|
+
* sendCommand(args: string[], options?: CommandOptions): Promise<unknown>
|
|
164
|
+
*
|
|
165
|
+
* Where args[0] is the command name and args[1..] are command arguments.
|
|
166
|
+
*
|
|
167
|
+
* @param original - The original sendCommand function
|
|
168
|
+
* @returns A wrapped version
|
|
169
|
+
*/
|
|
170
|
+
private _wrapNodeRedisSendCommand;
|
|
171
|
+
/**
|
|
172
|
+
* Extract connection info from an ioredis client instance.
|
|
173
|
+
*
|
|
174
|
+
* ioredis stores connection options on the instance as `this.options`.
|
|
175
|
+
*
|
|
176
|
+
* @param client - The ioredis client instance (`this` in the patched method)
|
|
177
|
+
* @returns Attributes object with connection info
|
|
178
|
+
*/
|
|
179
|
+
private _getIoRedisConnectionInfo;
|
|
180
|
+
/**
|
|
181
|
+
* Extract connection info from a @redis/client instance.
|
|
182
|
+
*
|
|
183
|
+
* node-redis v4+ stores connection options in various ways depending
|
|
184
|
+
* on the version. We try multiple paths defensively.
|
|
185
|
+
*
|
|
186
|
+
* @param client - The @redis/client instance (`this` in the patched method)
|
|
187
|
+
* @returns Attributes object with connection info
|
|
188
|
+
*/
|
|
189
|
+
private _getNodeRedisConnectionInfo;
|
|
190
|
+
}
|