@vtvlive/interactive-apm 0.0.2 → 0.0.3

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.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * OTLP Transport Type
3
+ *
4
+ * Defines the transport protocol used for exporting traces to APM
5
+ * - HTTP: Uses HTTP/JSON protocol (@opentelemetry/exporter-trace-otlp-http)
6
+ * - GRPC: Uses gRPC/protobuf protocol (@opentelemetry/exporter-trace-otlp-grpc)
7
+ * - PROTO: Uses protobuf over HTTP (@opentelemetry/exporter-trace-otlp-proto) - recommended for Elastic APM
8
+ */
9
+ export declare enum OtlpTransport {
10
+ HTTP = "http",
11
+ GRPC = "grpc",
12
+ PROTO = "proto"
13
+ }
14
+ //# sourceMappingURL=otlp-transport.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otlp-transport.type.d.ts","sourceRoot":"","sources":["../../src/types/otlp-transport.type.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,oBAAY,aAAa;IACvB,IAAI,SAAS;IACb,IAAI,SAAS;IACb,KAAK,UAAU;CAChB"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OtlpTransport = void 0;
4
+ /**
5
+ * OTLP Transport Type
6
+ *
7
+ * Defines the transport protocol used for exporting traces to APM
8
+ * - HTTP: Uses HTTP/JSON protocol (@opentelemetry/exporter-trace-otlp-http)
9
+ * - GRPC: Uses gRPC/protobuf protocol (@opentelemetry/exporter-trace-otlp-grpc)
10
+ * - PROTO: Uses protobuf over HTTP (@opentelemetry/exporter-trace-otlp-proto) - recommended for Elastic APM
11
+ */
12
+ var OtlpTransport;
13
+ (function (OtlpTransport) {
14
+ OtlpTransport["HTTP"] = "http";
15
+ OtlpTransport["GRPC"] = "grpc";
16
+ OtlpTransport["PROTO"] = "proto";
17
+ })(OtlpTransport || (exports.OtlpTransport = OtlpTransport = {}));
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Debug Exporter Wrapper for OpenTelemetry OTLP
3
+ *
4
+ * Wraps the OTLPTraceExporter to log full request/response details
5
+ * when APM_DEBUG=true. This helps debug connection issues with APM servers.
6
+ */
7
+ /**
8
+ * Wrap OTLPTraceExporter with debug logging
9
+ *
10
+ * This intercepts the export calls to log:
11
+ * - Outgoing request details (URL, headers, body)
12
+ * - Response details (status code, headers, body)
13
+ * - Success/failure of exports
14
+ *
15
+ * @param baseExporter The original OTLPTraceExporter instance
16
+ * @param endpointUrl The OTLP endpoint URL for logging
17
+ * @returns Wrapped exporter with debug capabilities
18
+ */
19
+ export declare function createDebugExporter(baseExporter: any, endpointUrl: string): any;
20
+ /**
21
+ * Create an HTTP interceptor for more detailed debugging
22
+ * This can be used to intercept the actual HTTP requests made by OTLP exporter
23
+ */
24
+ export declare function createHttpRequestInterceptor(): any;
25
+ //# sourceMappingURL=debug-exporter-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-exporter-wrapper.d.ts","sourceRoot":"","sources":["../../src/utils/debug-exporter-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,GAAG,GAAG,CA6E/E;AAyBD;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,GAAG,CA4FlD"}
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ /**
3
+ * Debug Exporter Wrapper for OpenTelemetry OTLP
4
+ *
5
+ * Wraps the OTLPTraceExporter to log full request/response details
6
+ * when APM_DEBUG=true. This helps debug connection issues with APM servers.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.createDebugExporter = createDebugExporter;
10
+ exports.createHttpRequestInterceptor = createHttpRequestInterceptor;
11
+ const debug_logger_1 = require("./debug-logger");
12
+ /**
13
+ * Wrap OTLPTraceExporter with debug logging
14
+ *
15
+ * This intercepts the export calls to log:
16
+ * - Outgoing request details (URL, headers, body)
17
+ * - Response details (status code, headers, body)
18
+ * - Success/failure of exports
19
+ *
20
+ * @param baseExporter The original OTLPTraceExporter instance
21
+ * @param endpointUrl The OTLP endpoint URL for logging
22
+ * @returns Wrapped exporter with debug capabilities
23
+ */
24
+ function createDebugExporter(baseExporter, endpointUrl) {
25
+ if (!(0, debug_logger_1.isDebugEnabled)()) {
26
+ // When debug is off, just log errors only
27
+ return {
28
+ export: (spans, resultCallback) => {
29
+ baseExporter.export(spans, (result) => {
30
+ if (result.error) {
31
+ (0, debug_logger_1.logExportFailure)('OpenTelemetry', result.error);
32
+ }
33
+ resultCallback(result);
34
+ });
35
+ },
36
+ shutdown: async () => {
37
+ await baseExporter.shutdown();
38
+ },
39
+ forceFlush: async () => {
40
+ if (baseExporter.forceFlush) {
41
+ await baseExporter.forceFlush();
42
+ }
43
+ },
44
+ };
45
+ }
46
+ // Debug mode ON - intercept everything
47
+ const wrappedExporter = {
48
+ export: (spans, resultCallback) => {
49
+ const startTime = Date.now();
50
+ // Log what we're about to send
51
+ (0, debug_logger_1.logRequest)('OpenTelemetry', endpointUrl, getExporterHeaders(baseExporter), {
52
+ spanCount: spans.length,
53
+ spans: spans.map((s) => ({
54
+ name: s.name,
55
+ kind: s.kind,
56
+ startTime: s.startTime,
57
+ endTime: s.endTime,
58
+ })),
59
+ });
60
+ // Wrap the result callback to capture response
61
+ baseExporter.export(spans, (result) => {
62
+ const duration = Date.now() - startTime;
63
+ if (result.error) {
64
+ (0, debug_logger_1.logExportFailure)('OpenTelemetry', result.error, {
65
+ endpoint: endpointUrl,
66
+ spanCount: spans.length,
67
+ duration: `${duration}ms`,
68
+ });
69
+ }
70
+ else {
71
+ (0, debug_logger_1.logExportSuccess)('OpenTelemetry', spans.length);
72
+ (0, debug_logger_1.logResponse)('OpenTelemetry', 200, { 'content-type': 'application/json' }, {
73
+ message: `Exported ${spans.length} span(s) in ${duration}ms`,
74
+ });
75
+ }
76
+ resultCallback(result);
77
+ });
78
+ },
79
+ shutdown: async () => {
80
+ (0, debug_logger_1.logRequest)('OpenTelemetry', endpointUrl, {}, { action: 'shutdown' });
81
+ try {
82
+ await baseExporter.shutdown();
83
+ (0, debug_logger_1.logResponse)('OpenTelemetry', 200, {}, { message: 'Exporter shutdown complete' });
84
+ }
85
+ catch (err) {
86
+ (0, debug_logger_1.logResponse)('OpenTelemetry', 500, {}, { message: 'Exporter shutdown failed', error: err?.message || err });
87
+ throw err;
88
+ }
89
+ },
90
+ forceFlush: async () => {
91
+ if (baseExporter.forceFlush) {
92
+ await baseExporter.forceFlush();
93
+ }
94
+ },
95
+ };
96
+ return wrappedExporter;
97
+ }
98
+ /**
99
+ * Get headers from the exporter instance for logging
100
+ * The OTLPTraceExporter stores headers internally, we need to extract them
101
+ */
102
+ function getExporterHeaders(exporter) {
103
+ try {
104
+ // Try to access headers from the exporter
105
+ // @ts-ignore - accessing internal property
106
+ if (exporter._headers) {
107
+ // @ts-ignore
108
+ return { ...exporter._headers };
109
+ }
110
+ // @ts-ignore - alternative internal property
111
+ if (exporter.headers) {
112
+ // @ts-ignore
113
+ return { ...exporter.headers };
114
+ }
115
+ }
116
+ catch (error) {
117
+ // Silently fail if we can't access headers
118
+ }
119
+ return {};
120
+ }
121
+ /**
122
+ * Create an HTTP interceptor for more detailed debugging
123
+ * This can be used to intercept the actual HTTP requests made by OTLP exporter
124
+ */
125
+ function createHttpRequestInterceptor() {
126
+ if (!(0, debug_logger_1.isDebugEnabled)()) {
127
+ return null;
128
+ }
129
+ const originalRequest = require('http').request;
130
+ const originalHttpsRequest = require('https').request;
131
+ return {
132
+ install: () => {
133
+ const { logRequest: logReq, logResponse: logResp } = require('./debug-logger');
134
+ // Override http.request
135
+ require('http').request = function (options, ...args) {
136
+ const url = typeof options === 'string' ? options : `${options.protocol || 'http:'}//${options.hostname || options.host}${options.path || '/'}`;
137
+ // Only intercept APM-related requests
138
+ if (url.includes('/v1/traces') || url.includes('/v1/metrics') || url.includes('/intake/v2/traces')) {
139
+ const req = originalRequest.call(this, options, ...args);
140
+ const originalWrite = req.write.bind(req);
141
+ req.write = function (chunk, ...rest) {
142
+ // Handle binary data safely - don't stringify protobuf/binary payloads
143
+ let logData;
144
+ if (Buffer.isBuffer(chunk) || chunk instanceof Uint8Array) {
145
+ logData = `<binary data: ${chunk.length} bytes>`;
146
+ }
147
+ else if (typeof chunk === 'string') {
148
+ logData = chunk;
149
+ }
150
+ else {
151
+ logData = chunk?.toString();
152
+ }
153
+ logReq('OpenTelemetry', url, options.headers || {}, logData);
154
+ return originalWrite(chunk, ...rest);
155
+ };
156
+ req.on('response', (res) => {
157
+ let body = '';
158
+ res.on('data', (chunk) => { body += chunk; });
159
+ res.on('end', () => {
160
+ logResp('OpenTelemetry', res.statusCode, res.headers, body);
161
+ });
162
+ });
163
+ return req;
164
+ }
165
+ return originalRequest.call(this, options, ...args);
166
+ };
167
+ // Override https.request
168
+ require('https').request = function (options, ...args) {
169
+ const url = typeof options === 'string' ? options : `${options.protocol || 'https:'}//${options.hostname || options.host}${options.path || '/'}`;
170
+ // Only intercept APM-related requests
171
+ if (url.includes('/v1/traces') || url.includes('/v1/metrics') || url.includes('/intake/v2/traces')) {
172
+ const req = originalHttpsRequest.call(this, options, ...args);
173
+ const originalWrite = req.write.bind(req);
174
+ req.write = function (chunk, ...rest) {
175
+ // Handle binary data safely - don't stringify protobuf/binary payloads
176
+ let logData;
177
+ if (Buffer.isBuffer(chunk) || chunk instanceof Uint8Array) {
178
+ logData = `<binary data: ${chunk.length} bytes>`;
179
+ }
180
+ else if (typeof chunk === 'string') {
181
+ logData = chunk;
182
+ }
183
+ else {
184
+ logData = chunk?.toString();
185
+ }
186
+ logReq('OpenTelemetry', url, options.headers || {}, logData);
187
+ return originalWrite(chunk, ...rest);
188
+ };
189
+ req.on('response', (res) => {
190
+ let body = '';
191
+ res.on('data', (chunk) => { body += chunk; });
192
+ res.on('end', () => {
193
+ logResp('OpenTelemetry', res.statusCode, res.headers, body);
194
+ });
195
+ });
196
+ return req;
197
+ }
198
+ return originalHttpsRequest.call(this, options, ...args);
199
+ };
200
+ },
201
+ uninstall: () => {
202
+ // Restore original functions
203
+ require('http').request = originalRequest;
204
+ require('https').request = originalHttpsRequest;
205
+ },
206
+ };
207
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Debug Logger for APM Package
3
+ *
4
+ * Provides centralized debug logging that only outputs when APM_DEBUG=true.
5
+ * Used by both Elastic APM and OpenTelemetry providers.
6
+ */
7
+ /**
8
+ * Check if debug mode is enabled via environment variable
9
+ */
10
+ export declare function isDebugEnabled(): boolean;
11
+ /**
12
+ * Sanitize headers for logging (mask sensitive values)
13
+ */
14
+ export declare function sanitizeHeaders(headers: Record<string, string>): Record<string, string>;
15
+ /**
16
+ * Sanitize config object for logging (mask sensitive values)
17
+ * Handles arrays and circular references safely
18
+ */
19
+ export declare function sanitizeConfig(config: any, seen?: WeakMap<object, any>): any;
20
+ /**
21
+ * Debug log - only outputs when APM_DEBUG=true
22
+ */
23
+ export declare function debugLog(message: string, ...args: any[]): void;
24
+ /**
25
+ * Info log - always outputs
26
+ */
27
+ export declare function infoLog(message: string, ...args: any[]): void;
28
+ /**
29
+ * Error log - always outputs
30
+ */
31
+ export declare function errorLog(message: string, ...args: any[]): void;
32
+ /**
33
+ * Log initialization details
34
+ */
35
+ export declare function logInitialization(provider: string, config: Record<string, any>): void;
36
+ /**
37
+ * Log span export success (debug mode only)
38
+ */
39
+ export declare function logExportSuccess(provider: string, spanCount: number): void;
40
+ /**
41
+ * Log span export failure - ALWAYS logs regardless of debug mode
42
+ */
43
+ export declare function logExportFailure(provider: string, error: any, requestDetails?: any): void;
44
+ /**
45
+ * Log outgoing HTTP request (for debugging APM communication)
46
+ */
47
+ export declare function logRequest(provider: string, url: string, headers: Record<string, string>, body?: any): void;
48
+ /**
49
+ * Log incoming HTTP response (for debugging APM communication)
50
+ */
51
+ export declare function logResponse(provider: string, statusCode: number, headers: Record<string, string>, body?: any): void;
52
+ /**
53
+ * Log span details (when debug mode is on)
54
+ */
55
+ export declare function logSpan(provider: string, span: any): void;
56
+ //# sourceMappingURL=debug-logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-logger.d.ts","sourceRoot":"","sources":["../../src/utils/debug-logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAExC;AAgBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMvF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,GAAE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAiB,GAAG,GAAG,CA8C3F;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAI9D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAE7D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAE9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAsBrF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAI1E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,EAAE,GAAG,GAAG,IAAI,CAQzF;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAwB3G;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI,CAUnH;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAmBzD"}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ /**
3
+ * Debug Logger for APM Package
4
+ *
5
+ * Provides centralized debug logging that only outputs when APM_DEBUG=true.
6
+ * Used by both Elastic APM and OpenTelemetry providers.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isDebugEnabled = isDebugEnabled;
10
+ exports.sanitizeHeaders = sanitizeHeaders;
11
+ exports.sanitizeConfig = sanitizeConfig;
12
+ exports.debugLog = debugLog;
13
+ exports.infoLog = infoLog;
14
+ exports.errorLog = errorLog;
15
+ exports.logInitialization = logInitialization;
16
+ exports.logExportSuccess = logExportSuccess;
17
+ exports.logExportFailure = logExportFailure;
18
+ exports.logRequest = logRequest;
19
+ exports.logResponse = logResponse;
20
+ exports.logSpan = logSpan;
21
+ /**
22
+ * Check if debug mode is enabled via environment variable
23
+ */
24
+ function isDebugEnabled() {
25
+ return process.env.APM_DEBUG === 'true';
26
+ }
27
+ /**
28
+ * Mask sensitive values in headers and config for logging
29
+ */
30
+ function maskSensitive(value, key) {
31
+ const sensitiveKeys = ['authorization', 'token', 'secret', 'password', 'apikey', 'api-key', 'x-api-key'];
32
+ const lowerKey = key.toLowerCase();
33
+ if (sensitiveKeys.some(k => lowerKey.includes(k))) {
34
+ return '[REDACTED]';
35
+ }
36
+ return value;
37
+ }
38
+ /**
39
+ * Sanitize headers for logging (mask sensitive values)
40
+ */
41
+ function sanitizeHeaders(headers) {
42
+ const sanitized = {};
43
+ for (const [key, value] of Object.entries(headers)) {
44
+ sanitized[key] = maskSensitive(value, key);
45
+ }
46
+ return sanitized;
47
+ }
48
+ /**
49
+ * Sanitize config object for logging (mask sensitive values)
50
+ * Handles arrays and circular references safely
51
+ */
52
+ function sanitizeConfig(config, seen = new WeakMap()) {
53
+ // Handle primitives and null
54
+ if (config === null || typeof config !== 'object') {
55
+ return config;
56
+ }
57
+ // Handle circular references
58
+ if (seen.has(config)) {
59
+ return '[Circular]';
60
+ }
61
+ seen.set(config, '[Circular]');
62
+ // Handle arrays
63
+ if (Array.isArray(config)) {
64
+ return config.map(item => {
65
+ if (typeof item === 'string') {
66
+ return maskSensitive(item, '');
67
+ }
68
+ else if (typeof item === 'object' && item !== null) {
69
+ return sanitizeConfig(item, seen);
70
+ }
71
+ return item;
72
+ });
73
+ }
74
+ // Handle objects
75
+ const sanitized = {};
76
+ for (const [key, value] of Object.entries(config)) {
77
+ if (typeof value === 'string') {
78
+ sanitized[key] = maskSensitive(value, key);
79
+ }
80
+ else if (Array.isArray(value)) {
81
+ sanitized[key] = value.map(item => {
82
+ if (typeof item === 'string') {
83
+ return maskSensitive(item, key);
84
+ }
85
+ else if (typeof item === 'object' && item !== null) {
86
+ return sanitizeConfig(item, seen);
87
+ }
88
+ return item;
89
+ });
90
+ }
91
+ else if (typeof value === 'object' && value !== null) {
92
+ sanitized[key] = sanitizeConfig(value, seen);
93
+ }
94
+ else {
95
+ sanitized[key] = value;
96
+ }
97
+ }
98
+ return sanitized;
99
+ }
100
+ /**
101
+ * Debug log - only outputs when APM_DEBUG=true
102
+ */
103
+ function debugLog(message, ...args) {
104
+ if (isDebugEnabled()) {
105
+ console.log(`[APM-DEBUG] ${message}`, ...args);
106
+ }
107
+ }
108
+ /**
109
+ * Info log - always outputs
110
+ */
111
+ function infoLog(message, ...args) {
112
+ console.log(`[APM] ${message}`, ...args);
113
+ }
114
+ /**
115
+ * Error log - always outputs
116
+ */
117
+ function errorLog(message, ...args) {
118
+ console.error(`[APM-ERROR] ${message}`, ...args);
119
+ }
120
+ /**
121
+ * Log initialization details
122
+ */
123
+ function logInitialization(provider, config) {
124
+ if (!isDebugEnabled()) {
125
+ return;
126
+ }
127
+ debugLog(`=== ${provider} Initialization ===`);
128
+ debugLog('Configuration:', sanitizeConfig(config));
129
+ debugLog('Environment Variables:', {
130
+ APM_PROVIDER: process.env.APM_PROVIDER,
131
+ ELASTIC_APM_SERVICE_NAME: process.env.ELASTIC_APM_SERVICE_NAME,
132
+ ELASTIC_APM_ENVIRONMENT: process.env.ELASTIC_APM_ENVIRONMENT,
133
+ ELASTIC_OTLP_ENDPOINT: process.env.ELASTIC_OTLP_ENDPOINT,
134
+ ELASTIC_APM_SERVER_URL: process.env.ELASTIC_APM_SERVER_URL,
135
+ APM_DEBUG: process.env.APM_DEBUG,
136
+ });
137
+ debugLog('System Info:', {
138
+ nodeVersion: process.version,
139
+ platform: process.platform,
140
+ arch: process.arch,
141
+ pid: process.pid,
142
+ hostname: require('os').hostname(),
143
+ });
144
+ }
145
+ /**
146
+ * Log span export success (debug mode only)
147
+ */
148
+ function logExportSuccess(provider, spanCount) {
149
+ if (isDebugEnabled()) {
150
+ debugLog(`[${provider}] Exported ${spanCount} span(s)`);
151
+ }
152
+ }
153
+ /**
154
+ * Log span export failure - ALWAYS logs regardless of debug mode
155
+ */
156
+ function logExportFailure(provider, error, requestDetails) {
157
+ // Always log export failures as errors
158
+ errorLog(`[${provider}] Export failed:`, error);
159
+ // Log additional details only in debug mode
160
+ if (isDebugEnabled() && requestDetails) {
161
+ debugLog('Request details:', sanitizeConfig(requestDetails));
162
+ }
163
+ }
164
+ /**
165
+ * Log outgoing HTTP request (for debugging APM communication)
166
+ */
167
+ function logRequest(provider, url, headers, body) {
168
+ if (!isDebugEnabled()) {
169
+ return;
170
+ }
171
+ debugLog(`[${provider}] → Sending request to: ${url}`);
172
+ debugLog('Headers:', sanitizeHeaders(headers));
173
+ // Sanitize request body for sensitive data before logging
174
+ if (body) {
175
+ // WARNING: Request bodies may contain sensitive data - we sanitize common patterns
176
+ let sanitizedBody;
177
+ if (typeof body === 'string') {
178
+ // Redact common auth/token patterns in strings
179
+ sanitizedBody = body.replace(/"(?:password|pass|pwd|token|access_token|auth|authorization|cookie|ssn|creditCard|cardNumber|cvv|secret)"\s*:\s*"[^"]*"/gi, '"$1": "[REDACTED]"');
180
+ sanitizedBody = sanitizedBody.replace(/Bearer\s+[A-Za-z0-9\-._~+/=]*/gi, 'Bearer [REDACTED]');
181
+ }
182
+ else if (typeof body === 'object' && body !== null) {
183
+ // Deep clone and sanitize object
184
+ sanitizedBody = sanitizeConfig(body);
185
+ }
186
+ else {
187
+ sanitizedBody = body;
188
+ }
189
+ debugLog('Body:', typeof sanitizedBody === 'string' ? sanitizedBody : JSON.stringify(sanitizedBody, null, 2));
190
+ }
191
+ }
192
+ /**
193
+ * Log incoming HTTP response (for debugging APM communication)
194
+ */
195
+ function logResponse(provider, statusCode, headers, body) {
196
+ if (!isDebugEnabled()) {
197
+ return;
198
+ }
199
+ debugLog(`[${provider}] ← Received response: ${statusCode}`);
200
+ debugLog('Response Headers:', sanitizeHeaders(headers));
201
+ if (body) {
202
+ debugLog('Response Body:', typeof body === 'string' ? body : JSON.stringify(body, null, 2));
203
+ }
204
+ }
205
+ /**
206
+ * Log span details (when debug mode is on)
207
+ */
208
+ function logSpan(provider, span) {
209
+ if (!isDebugEnabled()) {
210
+ return;
211
+ }
212
+ try {
213
+ const spanDetails = {
214
+ name: span.name,
215
+ kind: span.kind,
216
+ startTime: span.startTime,
217
+ endTime: span.endTime,
218
+ duration: span.endTime - span.startTime,
219
+ attributes: span.attributes,
220
+ status: span.status,
221
+ };
222
+ debugLog(`[${provider}] Span details:`, JSON.stringify(spanDetails, null, 2));
223
+ }
224
+ catch (error) {
225
+ debugLog(`[${provider}] Could not serialize span:`, error);
226
+ }
227
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtvlive/interactive-apm",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "APM integration package supporting both Elastic APM and OpenTelemetry with NestJS integration",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -35,6 +35,8 @@
35
35
  "@nestjs/config": "^1.0.0 || ^2.0.0 || ^3.0.0 || ^4.0.0",
36
36
  "@opentelemetry/api": "^1.0.0",
37
37
  "@opentelemetry/exporter-trace-otlp-http": ">=0.200.0",
38
+ "@opentelemetry/exporter-trace-otlp-grpc": ">=0.200.0",
39
+ "@opentelemetry/exporter-trace-otlp-proto": ">=0.200.0",
38
40
  "@opentelemetry/instrumentation-express": ">=0.50.0",
39
41
  "@opentelemetry/instrumentation-http": ">=0.200.0",
40
42
  "@opentelemetry/resources": ">=1.0.0",
@@ -56,6 +58,12 @@
56
58
  "@opentelemetry/exporter-trace-otlp-http": {
57
59
  "optional": true
58
60
  },
61
+ "@opentelemetry/exporter-trace-otlp-grpc": {
62
+ "optional": true
63
+ },
64
+ "@opentelemetry/exporter-trace-otlp-proto": {
65
+ "optional": true
66
+ },
59
67
  "@opentelemetry/instrumentation-express": {
60
68
  "optional": true
61
69
  },
@@ -83,6 +91,8 @@
83
91
  "@nestjs/config": "^4.0.3",
84
92
  "@opentelemetry/api": "^1.9.0",
85
93
  "@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
94
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
95
+ "@opentelemetry/exporter-trace-otlp-proto": "^1.22.0",
86
96
  "@opentelemetry/instrumentation-express": "^0.61.0",
87
97
  "@opentelemetry/instrumentation-http": "^0.213.0",
88
98
  "@opentelemetry/resources": "^2.6.0",