@vtvlive/interactive-apm 0.0.2

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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +320 -0
  3. package/dist/factories/tracing-provider.factory.d.ts +81 -0
  4. package/dist/factories/tracing-provider.factory.d.ts.map +1 -0
  5. package/dist/factories/tracing-provider.factory.js +93 -0
  6. package/dist/index.d.ts +11 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +35 -0
  9. package/dist/init/elastic-apm-init.d.ts +51 -0
  10. package/dist/init/elastic-apm-init.d.ts.map +1 -0
  11. package/dist/init/elastic-apm-init.js +86 -0
  12. package/dist/init/opentelemetry-init.d.ts +51 -0
  13. package/dist/init/opentelemetry-init.d.ts.map +1 -0
  14. package/dist/init/opentelemetry-init.js +156 -0
  15. package/dist/interfaces/tracing-provider.interface.d.ts +43 -0
  16. package/dist/interfaces/tracing-provider.interface.d.ts.map +1 -0
  17. package/dist/interfaces/tracing-provider.interface.js +2 -0
  18. package/dist/modules/tracing.module.d.ts +96 -0
  19. package/dist/modules/tracing.module.d.ts.map +1 -0
  20. package/dist/modules/tracing.module.js +162 -0
  21. package/dist/providers/elastic-apm.tracing-provider.d.ts +57 -0
  22. package/dist/providers/elastic-apm.tracing-provider.d.ts.map +1 -0
  23. package/dist/providers/elastic-apm.tracing-provider.js +163 -0
  24. package/dist/providers/opentelemetry.tracing-provider.d.ts +68 -0
  25. package/dist/providers/opentelemetry.tracing-provider.d.ts.map +1 -0
  26. package/dist/providers/opentelemetry.tracing-provider.js +288 -0
  27. package/dist/services/tracing.service.d.ts +88 -0
  28. package/dist/services/tracing.service.d.ts.map +1 -0
  29. package/dist/services/tracing.service.js +103 -0
  30. package/dist/types/apm-provider.type.d.ts +47 -0
  31. package/dist/types/apm-provider.type.d.ts.map +1 -0
  32. package/dist/types/apm-provider.type.js +32 -0
  33. package/dist/utils/tracing.helper.d.ts +68 -0
  34. package/dist/utils/tracing.helper.d.ts.map +1 -0
  35. package/dist/utils/tracing.helper.js +115 -0
  36. package/package.json +105 -0
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ElasticApmTracingProvider = void 0;
4
+ /**
5
+ * Elastic APM Tracing Provider Implementation
6
+ * Sử dụng elastic-apm-node agent để gửi traces về Elastic APM server
7
+ */
8
+ class ElasticApmTracingProvider {
9
+ constructor(config = {}) {
10
+ this.apm = null;
11
+ this.serviceName = config.serviceName || process.env.ELASTIC_APM_SERVICE_NAME || 'interactive-backend';
12
+ this.environment = config.environment || process.env.ELASTIC_APM_ENVIRONMENT || 'development';
13
+ }
14
+ /**
15
+ * Initialize Elastic APM agent
16
+ * Should be called before using the provider
17
+ */
18
+ initialize(config) {
19
+ try {
20
+ // @ts-ignore - Optional peer dependency
21
+ const apm = require('elastic-apm-node');
22
+ // If agent is already started, just get reference
23
+ if (apm.isStarted()) {
24
+ this.apm = apm;
25
+ return;
26
+ }
27
+ // Start the agent with provided config
28
+ this.apm = apm.start({
29
+ serviceName: config?.serviceName || this.serviceName,
30
+ serverUrl: config?.serverUrl || process.env.ELASTIC_APM_SERVER_URL || 'http://localhost:8200',
31
+ secretToken: config?.secretToken || process.env.ELASTIC_APM_SECRET_TOKEN,
32
+ serviceVersion: config?.serviceVersion || process.env.npm_package_version || '1.0.0',
33
+ environment: config?.environment || this.environment,
34
+ logLevel: 'error',
35
+ });
36
+ console.log(`[ElasticAPM] Service: ${this.serviceName}, Environment: ${this.environment}`);
37
+ console.log('[ElasticAPM] Initialized');
38
+ }
39
+ catch (error) {
40
+ console.error('[ElasticAPM] Failed to initialize:', error);
41
+ throw error;
42
+ }
43
+ }
44
+ /**
45
+ * Bắt đầu một span mới
46
+ */
47
+ startSpan(name, attributes, spanKind = 'INTERNAL') {
48
+ if (!this.apm) {
49
+ console.warn('[ElasticAPM] Not initialized');
50
+ return null;
51
+ }
52
+ // Elastic APM cần transaction để tạo span
53
+ let transaction = this.apm.currentTransaction;
54
+ let createdTransaction = false;
55
+ if (!transaction) {
56
+ const type = this.mapSpanKindToType(spanKind);
57
+ transaction = this.apm.startTransaction(name, type);
58
+ if (!transaction) {
59
+ return null;
60
+ }
61
+ createdTransaction = true;
62
+ }
63
+ const span = this.apm.startSpan(name, 'custom');
64
+ if (!span) {
65
+ if (createdTransaction && transaction) {
66
+ transaction.end();
67
+ }
68
+ return null;
69
+ }
70
+ // Set labels từ attributes
71
+ if (attributes) {
72
+ for (const [key, value] of Object.entries(attributes)) {
73
+ span.setLabel(key, String(value));
74
+ }
75
+ }
76
+ // Set span type dựa trên kind
77
+ span.setType(this.mapSpanKindToType(spanKind));
78
+ // Override end() để end transaction sau
79
+ const originalEnd = span.end.bind(span);
80
+ span.end = () => {
81
+ originalEnd();
82
+ if (createdTransaction && transaction) {
83
+ transaction.end();
84
+ }
85
+ };
86
+ return span;
87
+ }
88
+ /**
89
+ * Thực thi function với tracing context
90
+ */
91
+ async startSpanWithParent(name, fn, attributes) {
92
+ const span = this.startSpan(name, attributes);
93
+ try {
94
+ const result = await fn(span);
95
+ return result;
96
+ }
97
+ catch (error) {
98
+ this.apm?.captureError(error);
99
+ if (span) {
100
+ span.outcome = 'failure';
101
+ }
102
+ throw error;
103
+ }
104
+ finally {
105
+ if (span) {
106
+ span.end();
107
+ }
108
+ }
109
+ }
110
+ /**
111
+ * Capture error vào APM
112
+ */
113
+ captureError(error) {
114
+ if (this.apm) {
115
+ this.apm.captureError(error);
116
+ }
117
+ }
118
+ /**
119
+ * Set label cho current transaction/span
120
+ */
121
+ setAttribute(key, value) {
122
+ const span = this.apm?.currentSpan;
123
+ if (span) {
124
+ span.setLabel(key, String(value));
125
+ }
126
+ }
127
+ /**
128
+ * End span manually
129
+ */
130
+ endSpan(span) {
131
+ const activeSpan = span || this.apm?.currentSpan;
132
+ if (activeSpan) {
133
+ activeSpan.end();
134
+ }
135
+ }
136
+ /**
137
+ * Flush và shutdown gracefully
138
+ */
139
+ async shutdown() {
140
+ if (this.apm) {
141
+ await this.apm.flush();
142
+ }
143
+ }
144
+ /**
145
+ * Map string span kind to Elastic APM span type
146
+ */
147
+ mapSpanKindToType(kind) {
148
+ switch (kind.toUpperCase()) {
149
+ case 'SERVER':
150
+ return 'request';
151
+ case 'CLIENT':
152
+ return 'db';
153
+ case 'PRODUCER':
154
+ return 'messaging';
155
+ case 'CONSUMER':
156
+ return 'messaging';
157
+ case 'INTERNAL':
158
+ default:
159
+ return 'code';
160
+ }
161
+ }
162
+ }
163
+ exports.ElasticApmTracingProvider = ElasticApmTracingProvider;
@@ -0,0 +1,68 @@
1
+ import { ITracingProvider } from '../interfaces/tracing-provider.interface';
2
+ /**
3
+ * OpenTelemetry Tracing Provider Implementation
4
+ * Sử dụng OpenTelemetry SDK với OTLP exporter để gửi traces về Elastic APM
5
+ */
6
+ export declare class OpenTelemetryTracingProvider implements ITracingProvider {
7
+ private sdk;
8
+ private tracer;
9
+ private readonly serviceName;
10
+ private readonly environment;
11
+ private readonly otlpEndpoint;
12
+ private readonly secretToken;
13
+ private readonly otlpAuthToken;
14
+ private readonly otlpHeaders;
15
+ private readonly enableConsoleExporter;
16
+ private initialized;
17
+ constructor(config?: {
18
+ serviceName?: string;
19
+ environment?: string;
20
+ otlpEndpoint?: string;
21
+ secretToken?: string;
22
+ otlpAuthToken?: string;
23
+ otlpHeaders?: Record<string, string>;
24
+ enableConsoleExporter?: boolean;
25
+ });
26
+ /**
27
+ * Initialize OpenTelemetry SDK
28
+ * Should be called before using the provider
29
+ */
30
+ initialize(config?: {
31
+ serviceName?: string;
32
+ otlpEndpoint?: string;
33
+ secretToken?: string;
34
+ otlpAuthToken?: string;
35
+ otlpHeaders?: Record<string, string>;
36
+ enableConsoleExporter?: boolean;
37
+ environment?: string;
38
+ }): Promise<void>;
39
+ /**
40
+ * Bắt đầu một span mới
41
+ */
42
+ startSpan(name: string, attributes?: Record<string, string | number>, spanKind?: string): any;
43
+ /**
44
+ * Thực thi function với tracing context
45
+ */
46
+ startSpanWithParent<T>(name: string, fn: (span: any) => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
47
+ /**
48
+ * Capture error vào active span
49
+ */
50
+ captureError(error: Error): void;
51
+ /**
52
+ * Set attribute cho active span
53
+ */
54
+ setAttribute(key: string, value: string | number): void;
55
+ /**
56
+ * End span manually
57
+ */
58
+ endSpan(span?: any): void;
59
+ /**
60
+ * Shutdown gracefully
61
+ */
62
+ shutdown(): Promise<void>;
63
+ /**
64
+ * Map string span kind to OpenTelemetry SpanKind enum
65
+ */
66
+ private mapSpanKind;
67
+ }
68
+ //# sourceMappingURL=opentelemetry.tracing-provider.d.ts.map
@@ -0,0 +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;AAE5E;;;GAGG;AACH,qBAAa,4BAA6B,YAAW,gBAAgB;IACnE,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,MAAM,CAAa;IAC3B,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,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;gBAEhB,MAAM,GAAE;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,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;KAC5B;IAUN;;;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;IAwIjB;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,QAAQ,GAAE,MAAmB,GAAG,GAAG;IAqBzG;;OAEG;IACG,mBAAmB,CAAC,CAAC,EACzB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,EAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC3C,OAAO,CAAC,CAAC,CAAC;IAoBb;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAchC;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAavD;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAazB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/B;;OAEG;IACH,OAAO,CAAC,WAAW;CAiBpB"}
@@ -0,0 +1,288 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.OpenTelemetryTracingProvider = void 0;
37
+ /**
38
+ * OpenTelemetry Tracing Provider Implementation
39
+ * Sử dụng OpenTelemetry SDK với OTLP exporter để gửi traces về Elastic APM
40
+ */
41
+ class OpenTelemetryTracingProvider {
42
+ constructor(config = {}) {
43
+ this.sdk = null;
44
+ this.tracer = null;
45
+ this.initialized = false;
46
+ this.serviceName = config.serviceName || process.env.ELASTIC_APM_SERVICE_NAME || 'interactive-backend';
47
+ this.environment = config.environment || process.env.ELASTIC_APM_ENVIRONMENT || 'development';
48
+ this.otlpEndpoint = config.otlpEndpoint || process.env.ELASTIC_OTLP_ENDPOINT || 'http://localhost:8200/v1/traces';
49
+ this.secretToken = config.secretToken || process.env.ELASTIC_APM_SECRET_TOKEN || '';
50
+ this.otlpAuthToken = config.otlpAuthToken || process.env.ELASTIC_OTLP_AUTH_TOKEN || '';
51
+ this.otlpHeaders = config.otlpHeaders || {};
52
+ this.enableConsoleExporter = config.enableConsoleExporter ?? process.env.ELASTIC_OTLP_ENABLE_CONSOLE_EXPORTER === 'true';
53
+ }
54
+ /**
55
+ * Initialize OpenTelemetry SDK
56
+ * Should be called before using the provider
57
+ */
58
+ async initialize(config) {
59
+ if (this.initialized) {
60
+ return;
61
+ }
62
+ try {
63
+ // Dynamic import to avoid errors when @opentelemetry packages are not installed
64
+ // @ts-ignore - Optional peer dependency
65
+ const { NodeSDK } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/sdk-node')));
66
+ // @ts-ignore - Optional peer dependency
67
+ const { HttpInstrumentation } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/instrumentation-http')));
68
+ // @ts-ignore - Optional peer dependency
69
+ const { ExpressInstrumentation } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/instrumentation-express')));
70
+ // @ts-ignore - Optional peer dependency
71
+ const { OTLPTraceExporter } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/exporter-trace-otlp-http')));
72
+ // @ts-ignore - Optional peer dependency
73
+ const { resourceFromAttributes } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/resources')));
74
+ // @ts-ignore - Optional peer dependency
75
+ const { trace } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/api')));
76
+ console.log(`[OpenTelemetry] Service: ${this.serviceName}, Environment: ${this.environment}`);
77
+ console.log(`[OpenTelemetry] Endpoint: ${config?.otlpEndpoint || this.otlpEndpoint}`);
78
+ // OTLP Exporter - Build headers with Authorization
79
+ const buildHeaders = () => {
80
+ const headers = {
81
+ ...this.otlpHeaders,
82
+ ...(config?.otlpHeaders || {}),
83
+ };
84
+ const token = config?.otlpAuthToken || this.otlpAuthToken || config?.secretToken || this.secretToken;
85
+ if (token) {
86
+ headers['Authorization'] = `Bearer ${token}`;
87
+ }
88
+ return Object.keys(headers).length > 0 ? headers : undefined;
89
+ };
90
+ const otlpExporter = new OTLPTraceExporter({
91
+ url: config?.otlpEndpoint || this.otlpEndpoint,
92
+ headers: buildHeaders(),
93
+ });
94
+ // Wrap OTLP exporter - only log errors
95
+ const otlpExporterWithLogging = {
96
+ export: (spans, resultCallback) => {
97
+ otlpExporter.export(spans, (result) => {
98
+ if (result.error) {
99
+ const endpoint = config?.otlpEndpoint || this.otlpEndpoint;
100
+ console.error(`[OpenTelemetry] Export to ${endpoint} failed: ${result.error instanceof Error ? result.error.message : result.error}`);
101
+ }
102
+ resultCallback(result);
103
+ });
104
+ },
105
+ shutdown: async () => {
106
+ await otlpExporter.shutdown();
107
+ },
108
+ };
109
+ // Use console exporter if enabled
110
+ const shouldEnableConsole = config?.enableConsoleExporter ?? this.enableConsoleExporter;
111
+ let traceExporter = otlpExporterWithLogging;
112
+ if (shouldEnableConsole) {
113
+ // @ts-ignore - Optional peer dependency
114
+ const { ConsoleSpanExporter } = await Promise.resolve().then(() => __importStar(require('@opentelemetry/sdk-trace-base')));
115
+ const consoleExporter = new ConsoleSpanExporter();
116
+ // Combined Exporter để export ra cả console và OTLP
117
+ traceExporter = {
118
+ export: (spans, resultCallback) => {
119
+ let completed = 0;
120
+ let hasError = false;
121
+ const errors = [];
122
+ const checkComplete = () => {
123
+ if (completed === 2) {
124
+ resultCallback(hasError ? { code: 1, error: new Error(errors.map(e => e.message).join('; ')) } : { code: 0 });
125
+ }
126
+ };
127
+ // Console exporter
128
+ consoleExporter.export(spans, (result) => {
129
+ if (result.error) {
130
+ hasError = true;
131
+ errors.push(result.error);
132
+ }
133
+ completed++;
134
+ checkComplete();
135
+ });
136
+ // OTLP exporter with logging
137
+ otlpExporterWithLogging.export(spans, (result) => {
138
+ if (result.error) {
139
+ hasError = true;
140
+ errors.push(result.error);
141
+ }
142
+ completed++;
143
+ checkComplete();
144
+ });
145
+ },
146
+ shutdown: async () => {
147
+ await Promise.all([consoleExporter.shutdown(), otlpExporterWithLogging.shutdown()]);
148
+ },
149
+ };
150
+ }
151
+ // Resource attributes
152
+ const sdkResource = resourceFromAttributes({
153
+ 'service.name': config?.serviceName || this.serviceName,
154
+ 'deployment.environment': config?.environment || this.environment,
155
+ 'service.version': process.env.npm_package_version || '1.0.0',
156
+ 'service.instance.id': `${process.pid}`,
157
+ 'host.name': require('os').hostname(),
158
+ });
159
+ // Khởi tạo SDK
160
+ this.sdk = new NodeSDK({
161
+ serviceName: config?.serviceName || this.serviceName,
162
+ traceExporter,
163
+ instrumentations: [new HttpInstrumentation(), new ExpressInstrumentation()],
164
+ resource: sdkResource,
165
+ });
166
+ // Khởi động SDK
167
+ this.sdk.start();
168
+ // Get tracer
169
+ this.tracer = trace.getTracer(config?.serviceName || this.serviceName);
170
+ this.initialized = true;
171
+ console.log('[OpenTelemetry] Initialized');
172
+ }
173
+ catch (error) {
174
+ console.error('[OpenTelemetry] Failed to initialize:', error);
175
+ throw error;
176
+ }
177
+ }
178
+ /**
179
+ * Bắt đầu một span mới
180
+ */
181
+ startSpan(name, attributes, spanKind = 'INTERNAL') {
182
+ if (!this.initialized || !this.tracer) {
183
+ console.warn('[OpenTelemetry] Not initialized');
184
+ return null;
185
+ }
186
+ // @ts-ignore - Optional peer dependency
187
+ const { context, SpanKind } = require('@opentelemetry/api');
188
+ const span = this.tracer.startSpan(name, {
189
+ attributes,
190
+ kind: this.mapSpanKind(spanKind),
191
+ }, context.active());
192
+ return span;
193
+ }
194
+ /**
195
+ * Thực thi function với tracing context
196
+ */
197
+ async startSpanWithParent(name, fn, attributes) {
198
+ // @ts-ignore - Optional peer dependency
199
+ const { context, trace, SpanStatusCode } = require('@opentelemetry/api');
200
+ return context.with(trace.setSpan(context.active(), this.startSpan(name, attributes)), async () => {
201
+ const span = trace.getActiveSpan();
202
+ try {
203
+ const result = await fn(span);
204
+ return result;
205
+ }
206
+ catch (error) {
207
+ span?.recordException(error);
208
+ span?.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
209
+ throw error;
210
+ }
211
+ finally {
212
+ span?.end();
213
+ }
214
+ });
215
+ }
216
+ /**
217
+ * Capture error vào active span
218
+ */
219
+ captureError(error) {
220
+ if (!this.initialized) {
221
+ return;
222
+ }
223
+ // @ts-ignore - Optional peer dependency
224
+ const { trace, SpanStatusCode } = require('@opentelemetry/api');
225
+ const span = trace.getActiveSpan();
226
+ if (span) {
227
+ span.recordException(error);
228
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
229
+ }
230
+ }
231
+ /**
232
+ * Set attribute cho active span
233
+ */
234
+ setAttribute(key, value) {
235
+ if (!this.initialized) {
236
+ return;
237
+ }
238
+ // @ts-ignore - Optional peer dependency
239
+ const { trace } = require('@opentelemetry/api');
240
+ const span = trace.getActiveSpan();
241
+ if (span) {
242
+ span.setAttribute(key, value);
243
+ }
244
+ }
245
+ /**
246
+ * End span manually
247
+ */
248
+ endSpan(span) {
249
+ if (!this.initialized) {
250
+ return;
251
+ }
252
+ // @ts-ignore - Optional peer dependency
253
+ const { trace } = require('@opentelemetry/api');
254
+ const activeSpan = span || trace.getActiveSpan();
255
+ if (activeSpan) {
256
+ activeSpan.end();
257
+ }
258
+ }
259
+ /**
260
+ * Shutdown gracefully
261
+ */
262
+ async shutdown() {
263
+ if (this.sdk) {
264
+ await this.sdk.shutdown();
265
+ }
266
+ }
267
+ /**
268
+ * Map string span kind to OpenTelemetry SpanKind enum
269
+ */
270
+ mapSpanKind(kind) {
271
+ // @ts-ignore - Optional peer dependency
272
+ const { SpanKind } = require('@opentelemetry/api');
273
+ switch (kind.toUpperCase()) {
274
+ case 'SERVER':
275
+ return SpanKind.SERVER;
276
+ case 'CLIENT':
277
+ return SpanKind.CLIENT;
278
+ case 'PRODUCER':
279
+ return SpanKind.PRODUCER;
280
+ case 'CONSUMER':
281
+ return SpanKind.CONSUMER;
282
+ case 'INTERNAL':
283
+ default:
284
+ return SpanKind.INTERNAL;
285
+ }
286
+ }
287
+ }
288
+ exports.OpenTelemetryTracingProvider = OpenTelemetryTracingProvider;
@@ -0,0 +1,88 @@
1
+ import { ITracingProvider } from '../interfaces/tracing-provider.interface';
2
+ /**
3
+ * Tracing Service - Wrapper cho ITracingProvider
4
+ * Service này được inject vào controllers/services để sử dụng tracing
5
+ *
6
+ * Provider implementation (OpenTelemetry hoặc Elastic APM) được inject thông qua DI
7
+ *
8
+ * @example
9
+ * // NestJS usage
10
+ * constructor(private readonly tracingService: TracingService) {}
11
+ *
12
+ * someMethod() {
13
+ * const span = this.tracingService.startSpan('my.operation', { 'key': 'value' }, 'SERVER');
14
+ * try {
15
+ * // ... code
16
+ * } finally {
17
+ * span.end();
18
+ * }
19
+ * }
20
+ *
21
+ * @example
22
+ * // Standalone usage
23
+ * const provider = createTracingProvider();
24
+ * const tracingService = new TracingService(provider);
25
+ */
26
+ export declare class TracingService {
27
+ private readonly provider;
28
+ constructor(provider: ITracingProvider);
29
+ /**
30
+ * Bắt đầu một span mới để trace operation
31
+ * @param name Tên của span
32
+ * @param attributes Các attributes metadata
33
+ * @param spanKind Loại span (mặc định: INTERNAL)
34
+ * - INTERNAL: Internal operation
35
+ * - SERVER: Server-side request handler (API endpoints)
36
+ * - CLIENT: Client-side call (outgoing HTTP, database)
37
+ * - PRODUCER: Message producer
38
+ * - CONSUMER: Message consumer
39
+ * @returns Span object - cần gọi end() khi hoàn thành
40
+ *
41
+ * @example
42
+ * const span = this.tracingService.startSpan('my.operation', { 'key': 'value' }, 'SERVER');
43
+ * try {
44
+ * // ... code
45
+ * } finally {
46
+ * span.end();
47
+ * }
48
+ */
49
+ startSpan(name: string, attributes?: Record<string, string | number>, spanKind?: string): any;
50
+ /**
51
+ * Thực thi function với context tracing (auto-close span)
52
+ * @param name Tên của span
53
+ * @param fn Function cần trace
54
+ * @param attributes Các attributes metadata
55
+ * @returns Kết quả của function
56
+ *
57
+ * @example
58
+ * const result = await this.tracingService.startSpanWithParent(
59
+ * 'my.operation',
60
+ * async (span) => {
61
+ * span.setAttribute('custom', 'value');
62
+ * return await someAsyncOperation();
63
+ * }
64
+ * );
65
+ */
66
+ startSpanWithParent<T>(name: string, fn: (span: any) => Promise<T>, attributes?: Record<string, string | number>): Promise<T>;
67
+ /**
68
+ * Capture error vào active span hiện tại
69
+ * @param error Error object
70
+ */
71
+ captureError(error: Error): void;
72
+ /**
73
+ * Set attribute cho active span hiện tại
74
+ * @param key Tên attribute
75
+ * @param value Giá trị attribute
76
+ */
77
+ setAttribute(key: string, value: string | number): void;
78
+ /**
79
+ * Kết thúc span manually
80
+ * @param span Span cần end (nếu không truyền, sẽ end active span)
81
+ */
82
+ endSpan(span?: any): void;
83
+ /**
84
+ * Flush và shutdown APM provider (cho graceful shutdown)
85
+ */
86
+ shutdown(): Promise<void>;
87
+ }
88
+ //# sourceMappingURL=tracing.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracing.service.d.ts","sourceRoot":"","sources":["../../src/services/tracing.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,cAAc;IACb,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,gBAAgB;IAEvD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,QAAQ,GAAE,MAAmB,GAAG,GAAG;IAIzG;;;;;;;;;;;;;;;OAeG;IACH,mBAAmB,CAAC,CAAC,EACnB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,EAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAC3C,OAAO,CAAC,CAAC,CAAC;IAIb;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAIhC;;;;OAIG;IACH,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIvD;;;OAGG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;IAIzB;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC"}