@vtvlive/interactive-apm 0.0.2 → 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/README.md +201 -123
- package/dist/factories/tracing-provider.factory.d.ts +8 -3
- package/dist/factories/tracing-provider.factory.d.ts.map +1 -1
- package/dist/factories/tracing-provider.factory.js +17 -13
- package/dist/index.d.ts +12 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -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 +29 -12
- package/dist/init/opentelemetry-init.d.ts +8 -1
- package/dist/init/opentelemetry-init.d.ts.map +1 -1
- package/dist/init/opentelemetry-init.js +145 -44
- 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 +127 -28
- package/dist/providers/opentelemetry.tracing-provider.d.ts +12 -4
- package/dist/providers/opentelemetry.tracing-provider.d.ts.map +1 -1
- package/dist/providers/opentelemetry.tracing-provider.js +328 -67
- 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/types/otlp-transport.type.d.ts +14 -0
- package/dist/types/otlp-transport.type.d.ts.map +1 -0
- package/dist/types/otlp-transport.type.js +17 -0
- package/dist/utils/debug-exporter-wrapper.d.ts +36 -0
- package/dist/utils/debug-exporter-wrapper.d.ts.map +1 -0
- package/dist/utils/debug-exporter-wrapper.js +247 -0
- package/dist/utils/debug-logger.d.ts +81 -0
- package/dist/utils/debug-logger.d.ts.map +1 -0
- package/dist/utils/debug-logger.js +236 -0
- 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 +24 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ElasticApmTracingProvider = void 0;
|
|
4
|
+
const debug_logger_1 = require("../utils/debug-logger");
|
|
4
5
|
/**
|
|
5
6
|
* Elastic APM Tracing Provider Implementation
|
|
6
7
|
* Sử dụng elastic-apm-node agent để gửi traces về Elastic APM server
|
|
@@ -8,45 +9,65 @@ exports.ElasticApmTracingProvider = void 0;
|
|
|
8
9
|
class ElasticApmTracingProvider {
|
|
9
10
|
constructor(config = {}) {
|
|
10
11
|
this.apm = null;
|
|
11
|
-
this.serviceName =
|
|
12
|
-
|
|
12
|
+
this.serviceName =
|
|
13
|
+
config.serviceName || process.env.ELASTIC_APM_SERVICE_NAME || "interactive-backend";
|
|
14
|
+
this.environment = config.environment || process.env.ELASTIC_APM_ENVIRONMENT || "development";
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Initialize Elastic APM agent
|
|
16
18
|
* Should be called before using the provider
|
|
17
19
|
*/
|
|
18
|
-
initialize(config) {
|
|
20
|
+
async initialize(config) {
|
|
19
21
|
try {
|
|
20
22
|
// @ts-ignore - Optional peer dependency
|
|
21
|
-
const apm = require(
|
|
23
|
+
const apm = require("elastic-apm-node");
|
|
22
24
|
// If agent is already started, just get reference
|
|
23
25
|
if (apm.isStarted()) {
|
|
24
26
|
this.apm = apm;
|
|
27
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
28
|
+
console.log(`[APM-DEBUG] [ElasticAPM] Agent already started, using existing instance`);
|
|
29
|
+
}
|
|
25
30
|
return;
|
|
26
31
|
}
|
|
32
|
+
const serverUrl = config?.serverUrl || process.env.ELASTIC_APM_SERVER_URL || "http://localhost:8200";
|
|
33
|
+
const secretToken = config?.secretToken || process.env.ELASTIC_APM_SECRET_TOKEN;
|
|
34
|
+
const serviceVersion = config?.serviceVersion || process.env.npm_package_version || "1.0.0";
|
|
35
|
+
const environment = config?.environment || this.environment;
|
|
36
|
+
// Log initialization details when debug mode is on
|
|
37
|
+
(0, debug_logger_1.logInitialization)("ElasticAPM", {
|
|
38
|
+
serviceName: config?.serviceName || this.serviceName,
|
|
39
|
+
serverUrl,
|
|
40
|
+
hasSecretToken: !!secretToken,
|
|
41
|
+
serviceVersion,
|
|
42
|
+
environment,
|
|
43
|
+
});
|
|
27
44
|
// Start the agent with provided config
|
|
28
45
|
this.apm = apm.start({
|
|
29
46
|
serviceName: config?.serviceName || this.serviceName,
|
|
30
|
-
serverUrl
|
|
31
|
-
secretToken
|
|
32
|
-
serviceVersion
|
|
33
|
-
environment
|
|
34
|
-
logLevel:
|
|
47
|
+
serverUrl,
|
|
48
|
+
secretToken,
|
|
49
|
+
serviceVersion,
|
|
50
|
+
environment,
|
|
51
|
+
logLevel: (0, debug_logger_1.isDebugEnabled)() ? "info" : "error",
|
|
35
52
|
});
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
54
|
+
(0, debug_logger_1.infoLog)(`[ElasticAPM] Service: ${this.serviceName}, Environment: ${this.environment}`);
|
|
55
|
+
(0, debug_logger_1.infoLog)("[ElasticAPM] Initialized");
|
|
56
|
+
}
|
|
38
57
|
}
|
|
39
58
|
catch (error) {
|
|
40
|
-
|
|
59
|
+
(0, debug_logger_1.errorLog)("[ElasticAPM] Failed to initialize:", error);
|
|
41
60
|
throw error;
|
|
42
61
|
}
|
|
43
62
|
}
|
|
44
63
|
/**
|
|
45
64
|
* Bắt đầu một span mới
|
|
46
65
|
*/
|
|
47
|
-
startSpan(name, attributes, spanKind =
|
|
66
|
+
startSpan(name, attributes, spanKind = "INTERNAL") {
|
|
48
67
|
if (!this.apm) {
|
|
49
|
-
|
|
68
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
69
|
+
console.warn("[ElasticAPM] Not initialized");
|
|
70
|
+
}
|
|
50
71
|
return null;
|
|
51
72
|
}
|
|
52
73
|
// Elastic APM cần transaction để tạo span
|
|
@@ -60,7 +81,7 @@ class ElasticApmTracingProvider {
|
|
|
60
81
|
}
|
|
61
82
|
createdTransaction = true;
|
|
62
83
|
}
|
|
63
|
-
const span = this.apm.startSpan(name,
|
|
84
|
+
const span = this.apm.startSpan(name, "custom");
|
|
64
85
|
if (!span) {
|
|
65
86
|
if (createdTransaction && transaction) {
|
|
66
87
|
transaction.end();
|
|
@@ -75,10 +96,56 @@ class ElasticApmTracingProvider {
|
|
|
75
96
|
}
|
|
76
97
|
// Set span type dựa trên kind
|
|
77
98
|
span.setType(this.mapSpanKindToType(spanKind));
|
|
99
|
+
// Capture startTime for logging (will log when span ends)
|
|
100
|
+
const startTime = Date.now();
|
|
101
|
+
// Add end protection to prevent operations on ended span
|
|
102
|
+
let isEnded = false;
|
|
103
|
+
// Override setLabel to check if span has ended
|
|
104
|
+
const originalSetLabel = span.setLabel.bind(span);
|
|
105
|
+
span.setLabel = (key, value) => {
|
|
106
|
+
if (isEnded) {
|
|
107
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
108
|
+
console.warn(`[ElasticAPM] Cannot set label "${key}" on ended span "${name}". ` +
|
|
109
|
+
`This attribute will not be sent to APM server.`);
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
originalSetLabel(key, value);
|
|
114
|
+
};
|
|
115
|
+
// Override setAttribute (if it exists, maps to setLabel)
|
|
116
|
+
if (typeof span.setAttribute === "function") {
|
|
117
|
+
const originalSetAttribute = span.setAttribute.bind(span);
|
|
118
|
+
span.setAttribute = (key, value) => {
|
|
119
|
+
if (isEnded) {
|
|
120
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
121
|
+
console.warn(`[ElasticAPM] Cannot set attribute "${key}" on ended span "${name}". ` +
|
|
122
|
+
`This attribute will not be sent to APM server.`);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
originalSetAttribute(key, value);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
78
129
|
// Override end() để end transaction sau
|
|
79
130
|
const originalEnd = span.end.bind(span);
|
|
80
131
|
span.end = () => {
|
|
132
|
+
if (isEnded) {
|
|
133
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
134
|
+
console.warn(`[ElasticAPM] Span "${name}" has already been ended. Ignoring duplicate end().`);
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
isEnded = true;
|
|
139
|
+
// Log span details with actual duration
|
|
140
|
+
const endTime = Date.now();
|
|
141
|
+
(0, debug_logger_1.logSpan)("ElasticAPM", {
|
|
142
|
+
name: span.name,
|
|
143
|
+
kind: this.mapSpanKindToNumber(spanKind),
|
|
144
|
+
startTime,
|
|
145
|
+
endTime,
|
|
146
|
+
});
|
|
81
147
|
originalEnd();
|
|
148
|
+
// End transaction if we created it
|
|
82
149
|
if (createdTransaction && transaction) {
|
|
83
150
|
transaction.end();
|
|
84
151
|
}
|
|
@@ -97,7 +164,10 @@ class ElasticApmTracingProvider {
|
|
|
97
164
|
catch (error) {
|
|
98
165
|
this.apm?.captureError(error);
|
|
99
166
|
if (span) {
|
|
100
|
-
span.outcome =
|
|
167
|
+
span.outcome = "failure";
|
|
168
|
+
}
|
|
169
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
170
|
+
(0, debug_logger_1.infoLog)(`[ElasticAPM] Span failed: ${name} ${error.message || error}`);
|
|
101
171
|
}
|
|
102
172
|
throw error;
|
|
103
173
|
}
|
|
@@ -128,7 +198,7 @@ class ElasticApmTracingProvider {
|
|
|
128
198
|
* End span manually
|
|
129
199
|
*/
|
|
130
200
|
endSpan(span) {
|
|
131
|
-
const activeSpan = span || this.apm?.currentSpan;
|
|
201
|
+
const activeSpan = (span || this.apm?.currentSpan);
|
|
132
202
|
if (activeSpan) {
|
|
133
203
|
activeSpan.end();
|
|
134
204
|
}
|
|
@@ -138,7 +208,18 @@ class ElasticApmTracingProvider {
|
|
|
138
208
|
*/
|
|
139
209
|
async shutdown() {
|
|
140
210
|
if (this.apm) {
|
|
141
|
-
|
|
211
|
+
try {
|
|
212
|
+
if (this.apm.flush) {
|
|
213
|
+
await this.apm.flush();
|
|
214
|
+
}
|
|
215
|
+
if ((0, debug_logger_1.isDebugEnabled)()) {
|
|
216
|
+
(0, debug_logger_1.infoLog)("[ElasticAPM] Shutdown completed successfully");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
(0, debug_logger_1.errorLog)("[ElasticAPM] Shutdown failed:", error);
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
142
223
|
}
|
|
143
224
|
}
|
|
144
225
|
/**
|
|
@@ -146,17 +227,35 @@ class ElasticApmTracingProvider {
|
|
|
146
227
|
*/
|
|
147
228
|
mapSpanKindToType(kind) {
|
|
148
229
|
switch (kind.toUpperCase()) {
|
|
149
|
-
case
|
|
150
|
-
return
|
|
151
|
-
case
|
|
152
|
-
return
|
|
153
|
-
case
|
|
154
|
-
return
|
|
155
|
-
case
|
|
156
|
-
return
|
|
157
|
-
case
|
|
230
|
+
case "SERVER":
|
|
231
|
+
return "request";
|
|
232
|
+
case "CLIENT":
|
|
233
|
+
return "db";
|
|
234
|
+
case "PRODUCER":
|
|
235
|
+
return "messaging";
|
|
236
|
+
case "CONSUMER":
|
|
237
|
+
return "messaging";
|
|
238
|
+
case "INTERNAL":
|
|
239
|
+
default:
|
|
240
|
+
return "code";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Map string span kind to numeric value (for logging)
|
|
245
|
+
*/
|
|
246
|
+
mapSpanKindToNumber(kind) {
|
|
247
|
+
switch (kind.toUpperCase()) {
|
|
248
|
+
case "SERVER":
|
|
249
|
+
return 1;
|
|
250
|
+
case "CLIENT":
|
|
251
|
+
return 2;
|
|
252
|
+
case "PRODUCER":
|
|
253
|
+
return 3;
|
|
254
|
+
case "CONSUMER":
|
|
255
|
+
return 4;
|
|
256
|
+
case "INTERNAL":
|
|
158
257
|
default:
|
|
159
|
-
return
|
|
258
|
+
return 0;
|
|
160
259
|
}
|
|
161
260
|
}
|
|
162
261
|
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { ITracingProvider } from
|
|
1
|
+
import { ITracingProvider } from "../interfaces/tracing-provider.interface";
|
|
2
|
+
import { OtlpTransport } from "../types/otlp-transport.type";
|
|
3
|
+
import { ISpan } from "../types/apm.types";
|
|
2
4
|
/**
|
|
3
5
|
* OpenTelemetry Tracing Provider Implementation
|
|
4
6
|
* Sử dụng OpenTelemetry SDK với OTLP exporter để gửi traces về Elastic APM
|
|
@@ -9,6 +11,7 @@ export declare class OpenTelemetryTracingProvider implements ITracingProvider {
|
|
|
9
11
|
private readonly serviceName;
|
|
10
12
|
private readonly environment;
|
|
11
13
|
private readonly otlpEndpoint;
|
|
14
|
+
private readonly otlpTransport;
|
|
12
15
|
private readonly secretToken;
|
|
13
16
|
private readonly otlpAuthToken;
|
|
14
17
|
private readonly otlpHeaders;
|
|
@@ -18,6 +21,7 @@ export declare class OpenTelemetryTracingProvider implements ITracingProvider {
|
|
|
18
21
|
serviceName?: string;
|
|
19
22
|
environment?: string;
|
|
20
23
|
otlpEndpoint?: string;
|
|
24
|
+
otlpTransport?: OtlpTransport | string;
|
|
21
25
|
secretToken?: string;
|
|
22
26
|
otlpAuthToken?: string;
|
|
23
27
|
otlpHeaders?: Record<string, string>;
|
|
@@ -39,11 +43,11 @@ export declare class OpenTelemetryTracingProvider implements ITracingProvider {
|
|
|
39
43
|
/**
|
|
40
44
|
* Bắt đầu một span mới
|
|
41
45
|
*/
|
|
42
|
-
startSpan(name: string, attributes?: Record<string, string | number>, spanKind?: string):
|
|
46
|
+
startSpan(name: string, attributes?: Record<string, string | number>, spanKind?: string): ISpan | null;
|
|
43
47
|
/**
|
|
44
48
|
* Thực thi function với tracing context
|
|
45
49
|
*/
|
|
46
|
-
startSpanWithParent<T>(name: string, fn: (span:
|
|
50
|
+
startSpanWithParent<T>(name: string, fn: (span: ISpan | null) => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
|
|
47
51
|
/**
|
|
48
52
|
* Capture error vào active span
|
|
49
53
|
*/
|
|
@@ -52,10 +56,14 @@ export declare class OpenTelemetryTracingProvider implements ITracingProvider {
|
|
|
52
56
|
* Set attribute cho active span
|
|
53
57
|
*/
|
|
54
58
|
setAttribute(key: string, value: string | number): void;
|
|
59
|
+
/**
|
|
60
|
+
* Force flush all pending spans
|
|
61
|
+
*/
|
|
62
|
+
forceFlush(): Promise<void>;
|
|
55
63
|
/**
|
|
56
64
|
* End span manually
|
|
57
65
|
*/
|
|
58
|
-
endSpan(span?:
|
|
66
|
+
endSpan(span?: ISpan | null): void;
|
|
59
67
|
/**
|
|
60
68
|
* Shutdown gracefully
|
|
61
69
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opentelemetry.tracing-provider.d.ts","sourceRoot":"","sources":["../../src/providers/opentelemetry.tracing-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"opentelemetry.tracing-provider.d.ts","sourceRoot":"","sources":["../../src/providers/opentelemetry.tracing-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAyG3C;;;GAGG;AACH,qBAAa,4BAA6B,YAAW,gBAAgB;IACnE,OAAO,CAAC,GAAG,CAAiB;IAC5B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyB;IACrD,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAChD,OAAO,CAAC,WAAW,CAAS;gBAG1B,MAAM,GAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;QACvC,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;KAC5B;IA6BR;;;OAGG;IACG,UAAU,CAAC,MAAM,CAAC,EAAE;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,GAAG,OAAO,CAAC,IAAI,CAAC;IA+NjB;;OAEG;IACH,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC5C,QAAQ,GAAE,MAAmB,GAC5B,KAAK,GAAG,IAAI;IA+Gf;;OAEG;IACG,mBAAmB,CAAC,CAAC,EACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,IAAI,EAAE,KAAK,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EACtC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC3C,OAAO,CAAC,CAAC,CAAC;IAmCb;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAiBhC;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAavD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,IAAI;IAalC;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACH,OAAO,CAAC,WAAW;CAiBpB"}
|