@vtvlive/interactive-apm 0.0.3 → 0.0.4
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/factories/tracing-provider.factory.d.ts +6 -4
- package/dist/factories/tracing-provider.factory.d.ts.map +1 -1
- package/dist/factories/tracing-provider.factory.js +17 -14
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/init/elastic-apm-init.d.ts +25 -3
- package/dist/init/elastic-apm-init.d.ts.map +1 -1
- package/dist/init/elastic-apm-init.js +12 -12
- package/dist/init/opentelemetry-init.d.ts +6 -2
- package/dist/init/opentelemetry-init.d.ts.map +1 -1
- package/dist/init/opentelemetry-init.js +46 -42
- package/dist/interfaces/tracing-provider.interface.d.ts +13 -3
- package/dist/interfaces/tracing-provider.interface.d.ts.map +1 -1
- package/dist/modules/tracing.module.d.ts +5 -5
- package/dist/modules/tracing.module.d.ts.map +1 -1
- package/dist/modules/tracing.module.js +2 -1
- package/dist/providers/elastic-apm.tracing-provider.d.ts +23 -5
- package/dist/providers/elastic-apm.tracing-provider.d.ts.map +1 -1
- package/dist/providers/elastic-apm.tracing-provider.js +94 -30
- package/dist/providers/opentelemetry.tracing-provider.d.ts +6 -5
- package/dist/providers/opentelemetry.tracing-provider.d.ts.map +1 -1
- package/dist/providers/opentelemetry.tracing-provider.js +178 -85
- package/dist/services/tracing.service.d.ts +6 -5
- package/dist/services/tracing.service.d.ts.map +1 -1
- package/dist/services/tracing.service.js +2 -2
- package/dist/types/apm.types.d.ts +162 -0
- package/dist/types/apm.types.d.ts.map +1 -0
- package/dist/types/apm.types.js +37 -0
- package/dist/utils/debug-exporter-wrapper.d.ts +13 -2
- package/dist/utils/debug-exporter-wrapper.d.ts.map +1 -1
- package/dist/utils/debug-exporter-wrapper.js +82 -42
- package/dist/utils/debug-logger.d.ts +34 -9
- package/dist/utils/debug-logger.d.ts.map +1 -1
- package/dist/utils/debug-logger.js +37 -28
- package/dist/utils/tracing.helper.d.ts +2 -2
- package/dist/utils/tracing.helper.d.ts.map +1 -1
- package/dist/utils/tracing.helper.js +8 -4
- package/package.json +15 -4
|
@@ -22,16 +22,24 @@ exports.logSpan = logSpan;
|
|
|
22
22
|
* Check if debug mode is enabled via environment variable
|
|
23
23
|
*/
|
|
24
24
|
function isDebugEnabled() {
|
|
25
|
-
return process.env.APM_DEBUG ===
|
|
25
|
+
return process.env.APM_DEBUG === "true";
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Mask sensitive values in headers and config for logging
|
|
29
29
|
*/
|
|
30
30
|
function maskSensitive(value, key) {
|
|
31
|
-
const sensitiveKeys = [
|
|
31
|
+
const sensitiveKeys = [
|
|
32
|
+
"authorization",
|
|
33
|
+
"token",
|
|
34
|
+
"secret",
|
|
35
|
+
"password",
|
|
36
|
+
"apikey",
|
|
37
|
+
"api-key",
|
|
38
|
+
"x-api-key",
|
|
39
|
+
];
|
|
32
40
|
const lowerKey = key.toLowerCase();
|
|
33
41
|
if (sensitiveKeys.some(k => lowerKey.includes(k))) {
|
|
34
|
-
return
|
|
42
|
+
return "[REDACTED]";
|
|
35
43
|
}
|
|
36
44
|
return value;
|
|
37
45
|
}
|
|
@@ -51,21 +59,21 @@ function sanitizeHeaders(headers) {
|
|
|
51
59
|
*/
|
|
52
60
|
function sanitizeConfig(config, seen = new WeakMap()) {
|
|
53
61
|
// Handle primitives and null
|
|
54
|
-
if (config === null || typeof config !==
|
|
62
|
+
if (config === null || typeof config !== "object") {
|
|
55
63
|
return config;
|
|
56
64
|
}
|
|
57
65
|
// Handle circular references
|
|
58
66
|
if (seen.has(config)) {
|
|
59
|
-
return
|
|
67
|
+
return "[Circular]";
|
|
60
68
|
}
|
|
61
|
-
seen.set(config,
|
|
69
|
+
seen.set(config, "[Circular]");
|
|
62
70
|
// Handle arrays
|
|
63
71
|
if (Array.isArray(config)) {
|
|
64
72
|
return config.map(item => {
|
|
65
|
-
if (typeof item ===
|
|
66
|
-
return maskSensitive(item,
|
|
73
|
+
if (typeof item === "string") {
|
|
74
|
+
return maskSensitive(item, "");
|
|
67
75
|
}
|
|
68
|
-
else if (typeof item ===
|
|
76
|
+
else if (typeof item === "object" && item !== null) {
|
|
69
77
|
return sanitizeConfig(item, seen);
|
|
70
78
|
}
|
|
71
79
|
return item;
|
|
@@ -74,21 +82,21 @@ function sanitizeConfig(config, seen = new WeakMap()) {
|
|
|
74
82
|
// Handle objects
|
|
75
83
|
const sanitized = {};
|
|
76
84
|
for (const [key, value] of Object.entries(config)) {
|
|
77
|
-
if (typeof value ===
|
|
85
|
+
if (typeof value === "string") {
|
|
78
86
|
sanitized[key] = maskSensitive(value, key);
|
|
79
87
|
}
|
|
80
88
|
else if (Array.isArray(value)) {
|
|
81
89
|
sanitized[key] = value.map(item => {
|
|
82
|
-
if (typeof item ===
|
|
90
|
+
if (typeof item === "string") {
|
|
83
91
|
return maskSensitive(item, key);
|
|
84
92
|
}
|
|
85
|
-
else if (typeof item ===
|
|
93
|
+
else if (typeof item === "object" && item !== null) {
|
|
86
94
|
return sanitizeConfig(item, seen);
|
|
87
95
|
}
|
|
88
96
|
return item;
|
|
89
97
|
});
|
|
90
98
|
}
|
|
91
|
-
else if (typeof value ===
|
|
99
|
+
else if (typeof value === "object" && value !== null) {
|
|
92
100
|
sanitized[key] = sanitizeConfig(value, seen);
|
|
93
101
|
}
|
|
94
102
|
else {
|
|
@@ -125,8 +133,8 @@ function logInitialization(provider, config) {
|
|
|
125
133
|
return;
|
|
126
134
|
}
|
|
127
135
|
debugLog(`=== ${provider} Initialization ===`);
|
|
128
|
-
debugLog(
|
|
129
|
-
debugLog(
|
|
136
|
+
debugLog("Configuration:", sanitizeConfig(config));
|
|
137
|
+
debugLog("Environment Variables:", {
|
|
130
138
|
APM_PROVIDER: process.env.APM_PROVIDER,
|
|
131
139
|
ELASTIC_APM_SERVICE_NAME: process.env.ELASTIC_APM_SERVICE_NAME,
|
|
132
140
|
ELASTIC_APM_ENVIRONMENT: process.env.ELASTIC_APM_ENVIRONMENT,
|
|
@@ -134,12 +142,12 @@ function logInitialization(provider, config) {
|
|
|
134
142
|
ELASTIC_APM_SERVER_URL: process.env.ELASTIC_APM_SERVER_URL,
|
|
135
143
|
APM_DEBUG: process.env.APM_DEBUG,
|
|
136
144
|
});
|
|
137
|
-
debugLog(
|
|
145
|
+
debugLog("System Info:", {
|
|
138
146
|
nodeVersion: process.version,
|
|
139
147
|
platform: process.platform,
|
|
140
148
|
arch: process.arch,
|
|
141
149
|
pid: process.pid,
|
|
142
|
-
hostname: require(
|
|
150
|
+
hostname: require("os").hostname(),
|
|
143
151
|
});
|
|
144
152
|
}
|
|
145
153
|
/**
|
|
@@ -155,10 +163,11 @@ function logExportSuccess(provider, spanCount) {
|
|
|
155
163
|
*/
|
|
156
164
|
function logExportFailure(provider, error, requestDetails) {
|
|
157
165
|
// Always log export failures as errors
|
|
158
|
-
|
|
166
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
167
|
+
errorLog(`[${provider}] Export failed:`, errorMessage);
|
|
159
168
|
// Log additional details only in debug mode
|
|
160
169
|
if (isDebugEnabled() && requestDetails) {
|
|
161
|
-
debugLog(
|
|
170
|
+
debugLog("Request details:", sanitizeConfig(requestDetails));
|
|
162
171
|
}
|
|
163
172
|
}
|
|
164
173
|
/**
|
|
@@ -169,24 +178,24 @@ function logRequest(provider, url, headers, body) {
|
|
|
169
178
|
return;
|
|
170
179
|
}
|
|
171
180
|
debugLog(`[${provider}] → Sending request to: ${url}`);
|
|
172
|
-
debugLog(
|
|
181
|
+
debugLog("Headers:", sanitizeHeaders(headers));
|
|
173
182
|
// Sanitize request body for sensitive data before logging
|
|
174
183
|
if (body) {
|
|
175
184
|
// WARNING: Request bodies may contain sensitive data - we sanitize common patterns
|
|
176
185
|
let sanitizedBody;
|
|
177
|
-
if (typeof body ===
|
|
186
|
+
if (typeof body === "string") {
|
|
178
187
|
// Redact common auth/token patterns in strings
|
|
179
|
-
sanitizedBody = body.replace(/"(
|
|
180
|
-
sanitizedBody = sanitizedBody.replace(/Bearer\s+[A-Za-z0-9\-._~+/=]*/gi,
|
|
188
|
+
sanitizedBody = body.replace(/"(password|pass|pwd|token|access_token|auth|authorization|cookie|ssn|creditCard|cardNumber|cvv|secret)"\s*:\s*"[^"]*"/gi, '"$1": "[REDACTED]"');
|
|
189
|
+
sanitizedBody = sanitizedBody.replace(/Bearer\s+[A-Za-z0-9\-._~+/=]*/gi, "Bearer [REDACTED]");
|
|
181
190
|
}
|
|
182
|
-
else if (typeof body ===
|
|
191
|
+
else if (typeof body === "object" && body !== null) {
|
|
183
192
|
// Deep clone and sanitize object
|
|
184
193
|
sanitizedBody = sanitizeConfig(body);
|
|
185
194
|
}
|
|
186
195
|
else {
|
|
187
|
-
sanitizedBody = body;
|
|
196
|
+
sanitizedBody = String(body);
|
|
188
197
|
}
|
|
189
|
-
debugLog(
|
|
198
|
+
debugLog("Body:", typeof sanitizedBody === "string" ? sanitizedBody : JSON.stringify(sanitizedBody, null, 2));
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
/**
|
|
@@ -197,9 +206,9 @@ function logResponse(provider, statusCode, headers, body) {
|
|
|
197
206
|
return;
|
|
198
207
|
}
|
|
199
208
|
debugLog(`[${provider}] ← Received response: ${statusCode}`);
|
|
200
|
-
debugLog(
|
|
209
|
+
debugLog("Response Headers:", sanitizeHeaders(headers));
|
|
201
210
|
if (body) {
|
|
202
|
-
debugLog(
|
|
211
|
+
debugLog("Response Body:", typeof body === "string" ? body : JSON.stringify(body, null, 2));
|
|
203
212
|
}
|
|
204
213
|
}
|
|
205
214
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Span, SpanKind } from
|
|
1
|
+
import { Span, SpanKind } from "@opentelemetry/api";
|
|
2
2
|
/**
|
|
3
3
|
* TracingHelper - Static utility class for direct OpenTelemetry usage
|
|
4
4
|
*
|
|
@@ -37,7 +37,7 @@ export declare class TracingHelper {
|
|
|
37
37
|
* @param attributes Các attributes metadata
|
|
38
38
|
* @returns Kết quả của function
|
|
39
39
|
*/
|
|
40
|
-
static startSpanWithParent<T>(name: string, fn: (span: Span) => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
|
|
40
|
+
static startSpanWithParent<T>(name: string, fn: (span: Span | undefined) => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
|
|
41
41
|
/**
|
|
42
42
|
* Capture error vào active span hiện tại
|
|
43
43
|
* @param error Error object
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.helper.d.ts","sourceRoot":"","sources":["../../src/utils/tracing.helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,IAAI,EAAkB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAsC;IAE3D;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"tracing.helper.d.ts","sourceRoot":"","sources":["../../src/utils/tracing.helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,IAAI,EAAkB,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAsC;IAE3D;;;;;;OAMG;IACH,MAAM,CAAC,SAAS,CACd,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC5C,QAAQ,GAAE,QAA4B,GACrC,IAAI;IAaP;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAC1B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC,CAAC,EAC1C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC3C,OAAO,CAAC,CAAC,CAAC;IA2Bb;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAQvC;;;;OAIG;IACH,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAO9D;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI;IAOjC;;;OAGG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI,GAAG,SAAS;IAIxC;;;OAGG;IACH,MAAM,CAAC,UAAU,IAAI,MAAM,GAAG,SAAS;CAIxC"}
|
|
@@ -49,17 +49,21 @@ class TracingHelper {
|
|
|
49
49
|
static startSpanWithParent(name, fn, attributes) {
|
|
50
50
|
return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), TracingHelper.startSpan(name, attributes)), async () => {
|
|
51
51
|
const span = api_1.trace.getActiveSpan();
|
|
52
|
+
// If no active span, throw an error - this should not happen if the span was just started
|
|
53
|
+
if (!span) {
|
|
54
|
+
throw new Error(`Failed to get active span for "${name}". The OpenTelemetry context may not be properly initialized.`);
|
|
55
|
+
}
|
|
52
56
|
try {
|
|
53
57
|
const result = await fn(span);
|
|
54
58
|
return result;
|
|
55
59
|
}
|
|
56
60
|
catch (error) {
|
|
57
|
-
span
|
|
58
|
-
span
|
|
61
|
+
span.recordException(error);
|
|
62
|
+
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
|
|
59
63
|
throw error;
|
|
60
64
|
}
|
|
61
65
|
finally {
|
|
62
|
-
span
|
|
66
|
+
span.end();
|
|
63
67
|
}
|
|
64
68
|
});
|
|
65
69
|
}
|
|
@@ -112,4 +116,4 @@ class TracingHelper {
|
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
118
|
exports.TracingHelper = TracingHelper;
|
|
115
|
-
TracingHelper.tracer = api_1.trace.getTracer(
|
|
119
|
+
TracingHelper.tracer = api_1.trace.getTracer("interactive-apm");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtvlive/interactive-apm",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|
|
@@ -28,7 +28,12 @@
|
|
|
28
28
|
"test:watch": "jest --watch",
|
|
29
29
|
"test:coverage": "jest --coverage",
|
|
30
30
|
"test:unit": "jest __tests__/unit",
|
|
31
|
-
"test:integration": "jest __tests__/integration"
|
|
31
|
+
"test:integration": "jest __tests__/integration",
|
|
32
|
+
"lint": "eslint src",
|
|
33
|
+
"lint:fix": "eslint src --fix",
|
|
34
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
35
|
+
"format:check": "prettier --check \"src/**/*.ts\"",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
32
37
|
},
|
|
33
38
|
"peerDependencies": {
|
|
34
39
|
"@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0",
|
|
@@ -92,7 +97,7 @@
|
|
|
92
97
|
"@opentelemetry/api": "^1.9.0",
|
|
93
98
|
"@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
|
|
94
99
|
"@opentelemetry/exporter-trace-otlp-grpc": "^0.213.0",
|
|
95
|
-
"@opentelemetry/exporter-trace-otlp-proto": "^
|
|
100
|
+
"@opentelemetry/exporter-trace-otlp-proto": "^0.213.0",
|
|
96
101
|
"@opentelemetry/instrumentation-express": "^0.61.0",
|
|
97
102
|
"@opentelemetry/instrumentation-http": "^0.213.0",
|
|
98
103
|
"@opentelemetry/resources": "^2.6.0",
|
|
@@ -102,9 +107,15 @@
|
|
|
102
107
|
"@types/jest": "^29.5.0",
|
|
103
108
|
"@types/node": "^20.0.0",
|
|
104
109
|
"elastic-apm-node": "^4.15.0",
|
|
110
|
+
"eslint": "^9.18.0",
|
|
111
|
+
"eslint-config-prettier": "^9.1.0",
|
|
112
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
105
113
|
"jest": "^29.7.0",
|
|
114
|
+
"prettier": "^3.4.2",
|
|
106
115
|
"ts-jest": "^29.1.0",
|
|
107
|
-
"typescript": "^5.3.0"
|
|
116
|
+
"typescript": "^5.3.0",
|
|
117
|
+
"@typescript-eslint/eslint-plugin": "^8.19.1",
|
|
118
|
+
"@typescript-eslint/parser": "^8.19.1"
|
|
108
119
|
},
|
|
109
120
|
"engines": {
|
|
110
121
|
"node": ">=14.0.0"
|