@zucker-framework/tracing 1.0.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/index.js ADDED
@@ -0,0 +1,504 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ AlwaysOffSampler: () => AlwaysOffSampler,
25
+ AlwaysOnSampler: () => AlwaysOnSampler,
26
+ InMemoryTracer: () => InMemoryTracer,
27
+ ParentBasedSampler: () => ParentBasedSampler,
28
+ RatioSampler: () => RatioSampler,
29
+ SamplingDecision: () => SamplingDecision,
30
+ Span: () => Span,
31
+ SpanBuilder: () => SpanBuilder,
32
+ TraceInterceptor: () => TraceInterceptor,
33
+ TraceMiddleware: () => TraceMiddleware,
34
+ ZuckerTracingModule: () => ZuckerTracingModule,
35
+ generateTraceparent: () => generateTraceparent,
36
+ getSpanId: () => getSpanId,
37
+ getTraceContext: () => getTraceContext,
38
+ getTraceId: () => getTraceId,
39
+ runWithTraceContext: () => runWithTraceContext
40
+ });
41
+ module.exports = __toCommonJS(index_exports);
42
+
43
+ // src/span.ts
44
+ var import_crypto = require("crypto");
45
+ var Span = class {
46
+ static {
47
+ __name(this, "Span");
48
+ }
49
+ traceId;
50
+ spanId;
51
+ parentSpanId;
52
+ name;
53
+ startTime;
54
+ endTime;
55
+ attributes;
56
+ events = [];
57
+ links = [];
58
+ status = "UNSET";
59
+ statusMessage;
60
+ constructor(name, options) {
61
+ this.name = name;
62
+ this.traceId = options?.traceId ?? (0, import_crypto.randomUUID)().replace(/-/g, "");
63
+ this.spanId = (0, import_crypto.randomUUID)().replace(/-/g, "").substring(0, 16);
64
+ this.parentSpanId = options?.parentSpanId;
65
+ this.startTime = /* @__PURE__ */ new Date();
66
+ this.attributes = options?.attributes ?? {};
67
+ }
68
+ setAttribute(key, value) {
69
+ this.attributes[key] = value;
70
+ return this;
71
+ }
72
+ setAttributes(attrs) {
73
+ Object.assign(this.attributes, attrs);
74
+ return this;
75
+ }
76
+ addEvent(name, attributes) {
77
+ this.events.push({
78
+ name,
79
+ timestamp: /* @__PURE__ */ new Date(),
80
+ attributes
81
+ });
82
+ return this;
83
+ }
84
+ addLink(traceId, spanId, attributes) {
85
+ this.links.push({
86
+ traceId,
87
+ spanId,
88
+ attributes
89
+ });
90
+ return this;
91
+ }
92
+ setStatus(status, message) {
93
+ this.status = status;
94
+ this.statusMessage = message;
95
+ return this;
96
+ }
97
+ end() {
98
+ this.endTime = /* @__PURE__ */ new Date();
99
+ }
100
+ get duration() {
101
+ if (!this.endTime) return void 0;
102
+ return this.endTime.getTime() - this.startTime.getTime();
103
+ }
104
+ isRecording() {
105
+ return !this.endTime;
106
+ }
107
+ };
108
+ var SpanBuilder = class {
109
+ static {
110
+ __name(this, "SpanBuilder");
111
+ }
112
+ _name;
113
+ options = {};
114
+ _links = [];
115
+ constructor(name) {
116
+ this._name = name;
117
+ }
118
+ withTraceId(traceId) {
119
+ this.options.traceId = traceId;
120
+ return this;
121
+ }
122
+ withParentSpanId(parentSpanId) {
123
+ this.options.parentSpanId = parentSpanId;
124
+ return this;
125
+ }
126
+ withAttribute(key, value) {
127
+ if (!this.options.attributes) this.options.attributes = {};
128
+ this.options.attributes[key] = value;
129
+ return this;
130
+ }
131
+ withLink(traceId, spanId, attributes) {
132
+ this._links.push({
133
+ traceId,
134
+ spanId,
135
+ attributes
136
+ });
137
+ return this;
138
+ }
139
+ build() {
140
+ const span = new Span(this._name, this.options);
141
+ for (const link of this._links) {
142
+ span.addLink(link.traceId, link.spanId, link.attributes);
143
+ }
144
+ return span;
145
+ }
146
+ };
147
+
148
+ // src/tracer.ts
149
+ var import_common = require("@nestjs/common");
150
+ function _ts_decorate(decorators, target, key, desc) {
151
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
152
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
153
+ r = Reflect.decorate(decorators, target, key, desc);
154
+ } else {
155
+ for (var i = decorators.length - 1; i >= 0; i--) {
156
+ if (d = decorators[i]) {
157
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
158
+ }
159
+ }
160
+ }
161
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
162
+ }
163
+ __name(_ts_decorate, "_ts_decorate");
164
+ function _ts_metadata(metadataKey, metadataValue) {
165
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
166
+ return Reflect.metadata(metadataKey, metadataValue);
167
+ }
168
+ }
169
+ __name(_ts_metadata, "_ts_metadata");
170
+ var SamplingDecision = /* @__PURE__ */ (function(SamplingDecision2) {
171
+ SamplingDecision2["DROP"] = "DROP";
172
+ SamplingDecision2["RECORD_ONLY"] = "RECORD_ONLY";
173
+ SamplingDecision2["RECORD_AND_SAMPLE"] = "RECORD_AND_SAMPLE";
174
+ return SamplingDecision2;
175
+ })({});
176
+ var AlwaysOnSampler = class {
177
+ static {
178
+ __name(this, "AlwaysOnSampler");
179
+ }
180
+ shouldSample() {
181
+ return "RECORD_AND_SAMPLE";
182
+ }
183
+ };
184
+ var AlwaysOffSampler = class {
185
+ static {
186
+ __name(this, "AlwaysOffSampler");
187
+ }
188
+ shouldSample() {
189
+ return "DROP";
190
+ }
191
+ };
192
+ var RatioSampler = class {
193
+ static {
194
+ __name(this, "RatioSampler");
195
+ }
196
+ ratio;
197
+ constructor(ratio) {
198
+ this.ratio = ratio;
199
+ this.ratio = Math.max(0, Math.min(1, ratio));
200
+ }
201
+ shouldSample() {
202
+ return Math.random() < this.ratio ? "RECORD_AND_SAMPLE" : "DROP";
203
+ }
204
+ };
205
+ var ParentBasedSampler = class {
206
+ static {
207
+ __name(this, "ParentBasedSampler");
208
+ }
209
+ root;
210
+ constructor(root) {
211
+ this.root = root;
212
+ }
213
+ shouldSample(spanName, options) {
214
+ if (options?.parentSpanId) {
215
+ return "RECORD_AND_SAMPLE";
216
+ }
217
+ return this.root.shouldSample(spanName, options);
218
+ }
219
+ };
220
+ var InMemoryTracer = class {
221
+ static {
222
+ __name(this, "InMemoryTracer");
223
+ }
224
+ spans = [];
225
+ maxSpans = 1e4;
226
+ sampler;
227
+ constructor(sampler) {
228
+ this.sampler = sampler ?? new AlwaysOnSampler();
229
+ }
230
+ setSampler(sampler) {
231
+ this.sampler = sampler;
232
+ }
233
+ startSpan(name, options) {
234
+ const span = new Span(name, options);
235
+ const decision = this.sampler.shouldSample(name, options);
236
+ if (decision === "DROP") {
237
+ return span;
238
+ }
239
+ this.spans.push(span);
240
+ if (this.spans.length > this.maxSpans) {
241
+ this.spans = this.spans.slice(-this.maxSpans / 2);
242
+ }
243
+ return span;
244
+ }
245
+ getSpans() {
246
+ return [
247
+ ...this.spans
248
+ ];
249
+ }
250
+ getSpansByTraceId(traceId) {
251
+ return this.spans.filter((s) => s.traceId === traceId);
252
+ }
253
+ clear() {
254
+ this.spans = [];
255
+ }
256
+ };
257
+ InMemoryTracer = _ts_decorate([
258
+ (0, import_common.Injectable)(),
259
+ _ts_metadata("design:type", Function),
260
+ _ts_metadata("design:paramtypes", [
261
+ typeof Sampler === "undefined" ? Object : Sampler
262
+ ])
263
+ ], InMemoryTracer);
264
+
265
+ // src/trace-middleware.ts
266
+ var import_common2 = require("@nestjs/common");
267
+ var import_async_hooks = require("async_hooks");
268
+ var import_crypto2 = require("crypto");
269
+ function _ts_decorate2(decorators, target, key, desc) {
270
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
271
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
272
+ r = Reflect.decorate(decorators, target, key, desc);
273
+ } else {
274
+ for (var i = decorators.length - 1; i >= 0; i--) {
275
+ if (d = decorators[i]) {
276
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
277
+ }
278
+ }
279
+ }
280
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
281
+ }
282
+ __name(_ts_decorate2, "_ts_decorate");
283
+ var traceStorage = new import_async_hooks.AsyncLocalStorage();
284
+ function getTraceId() {
285
+ return traceStorage.getStore()?.traceId;
286
+ }
287
+ __name(getTraceId, "getTraceId");
288
+ function getSpanId() {
289
+ return traceStorage.getStore()?.parentSpanId;
290
+ }
291
+ __name(getSpanId, "getSpanId");
292
+ function getTraceContext() {
293
+ return traceStorage.getStore();
294
+ }
295
+ __name(getTraceContext, "getTraceContext");
296
+ function runWithTraceContext(ctx, fn) {
297
+ return traceStorage.run(ctx, fn);
298
+ }
299
+ __name(runWithTraceContext, "runWithTraceContext");
300
+ function generateTraceparent(traceId, spanId, sampled = true) {
301
+ const flags = sampled ? "01" : "00";
302
+ return `00-${traceId}-${spanId}-${flags}`;
303
+ }
304
+ __name(generateTraceparent, "generateTraceparent");
305
+ function parseTraceparent(header) {
306
+ const parts = header.split("-");
307
+ if (parts.length !== 4) return null;
308
+ const [version, traceId, parentId, flags] = parts;
309
+ if (version !== "00") return null;
310
+ if (traceId.length !== 32 || parentId.length !== 16) return null;
311
+ if (!/^[0-9a-fA-F]{32}$/.test(traceId) || !/^[0-9a-fA-F]{16}$/.test(parentId)) return null;
312
+ return {
313
+ traceId,
314
+ parentSpanId: parentId,
315
+ traceFlags: flags
316
+ };
317
+ }
318
+ __name(parseTraceparent, "parseTraceparent");
319
+ var TraceMiddleware = class {
320
+ static {
321
+ __name(this, "TraceMiddleware");
322
+ }
323
+ use(req, res, next) {
324
+ const traceparent = req.headers["traceparent"];
325
+ const tracestate = req.headers["tracestate"];
326
+ let ctx;
327
+ if (traceparent) {
328
+ const parsed = parseTraceparent(traceparent);
329
+ ctx = parsed ?? {
330
+ traceId: (0, import_crypto2.randomUUID)().replace(/-/g, ""),
331
+ traceFlags: "01"
332
+ };
333
+ } else {
334
+ ctx = {
335
+ traceId: (0, import_crypto2.randomUUID)().replace(/-/g, ""),
336
+ traceFlags: "01"
337
+ };
338
+ }
339
+ if (tracestate) {
340
+ ctx.traceState = tracestate;
341
+ }
342
+ const spanId = (0, import_crypto2.randomUUID)().replace(/-/g, "").substring(0, 16);
343
+ res.setHeader("traceparent", generateTraceparent(ctx.traceId, spanId, ctx.traceFlags === "01"));
344
+ if (ctx.traceState) {
345
+ res.setHeader("tracestate", ctx.traceState);
346
+ }
347
+ traceStorage.run(ctx, () => {
348
+ next();
349
+ });
350
+ }
351
+ };
352
+ TraceMiddleware = _ts_decorate2([
353
+ (0, import_common2.Injectable)()
354
+ ], TraceMiddleware);
355
+
356
+ // src/trace-interceptor.ts
357
+ var import_common3 = require("@nestjs/common");
358
+ var import_rxjs = require("rxjs");
359
+ var import_operators = require("rxjs/operators");
360
+ function _ts_decorate3(decorators, target, key, desc) {
361
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
362
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
363
+ r = Reflect.decorate(decorators, target, key, desc);
364
+ } else {
365
+ for (var i = decorators.length - 1; i >= 0; i--) {
366
+ if (d = decorators[i]) {
367
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
368
+ }
369
+ }
370
+ }
371
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
372
+ }
373
+ __name(_ts_decorate3, "_ts_decorate");
374
+ function _ts_metadata2(metadataKey, metadataValue) {
375
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
376
+ return Reflect.metadata(metadataKey, metadataValue);
377
+ }
378
+ }
379
+ __name(_ts_metadata2, "_ts_metadata");
380
+ var TraceInterceptor = class {
381
+ static {
382
+ __name(this, "TraceInterceptor");
383
+ }
384
+ tracer;
385
+ constructor(tracer) {
386
+ this.tracer = tracer;
387
+ }
388
+ intercept(context, next) {
389
+ const request = context.switchToHttp().getRequest();
390
+ const traceCtx = getTraceContext();
391
+ const routePath = request.route?.path ?? request.url;
392
+ const spanName = `${request.method} ${routePath}`;
393
+ const span = this.tracer.startSpan(spanName, {
394
+ traceId: traceCtx?.traceId,
395
+ parentSpanId: traceCtx?.parentSpanId,
396
+ attributes: {
397
+ "http.method": request.method,
398
+ "http.url": request.url,
399
+ "http.route": routePath,
400
+ "http.user_agent": request.headers?.["user-agent"],
401
+ "http.client_ip": request.ip || request.headers?.["x-forwarded-for"] || ""
402
+ }
403
+ });
404
+ return next.handle().pipe((0, import_operators.tap)(() => {
405
+ const response = context.switchToHttp().getResponse();
406
+ const statusCode = response?.statusCode ?? 200;
407
+ span.setAttribute("http.status_code", statusCode);
408
+ span.setStatus(statusCode < 400 ? "OK" : "ERROR");
409
+ span.end();
410
+ }), (0, import_operators.catchError)((error) => {
411
+ const statusCode = error?.status ?? 500;
412
+ span.setAttribute("http.status_code", statusCode);
413
+ span.setAttribute("error.type", error?.constructor?.name ?? "Error");
414
+ span.setAttribute("error.message", error instanceof Error ? error.message : String(error));
415
+ span.setStatus("ERROR", error instanceof Error ? error.message : String(error));
416
+ span.addEvent("exception", {
417
+ "exception.type": error?.constructor?.name ?? "Error",
418
+ "exception.message": error instanceof Error ? error.message : String(error),
419
+ "exception.stacktrace": error instanceof Error ? error.stack : void 0
420
+ });
421
+ span.end();
422
+ return (0, import_rxjs.throwError)(() => error);
423
+ }));
424
+ }
425
+ };
426
+ TraceInterceptor = _ts_decorate3([
427
+ (0, import_common3.Injectable)(),
428
+ _ts_metadata2("design:type", Function),
429
+ _ts_metadata2("design:paramtypes", [
430
+ typeof InMemoryTracer === "undefined" ? Object : InMemoryTracer
431
+ ])
432
+ ], TraceInterceptor);
433
+
434
+ // src/tracing.module.ts
435
+ var import_common4 = require("@nestjs/common");
436
+ var import_core = require("@nestjs/core");
437
+ function _ts_decorate4(decorators, target, key, desc) {
438
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
439
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
440
+ r = Reflect.decorate(decorators, target, key, desc);
441
+ } else {
442
+ for (var i = decorators.length - 1; i >= 0; i--) {
443
+ if (d = decorators[i]) {
444
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
445
+ }
446
+ }
447
+ }
448
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
449
+ }
450
+ __name(_ts_decorate4, "_ts_decorate");
451
+ var ZuckerTracingModule = class _ZuckerTracingModule {
452
+ static {
453
+ __name(this, "ZuckerTracingModule");
454
+ }
455
+ configure(consumer) {
456
+ consumer.apply(TraceMiddleware).forRoutes("*");
457
+ }
458
+ static forRoot(options) {
459
+ const sampleRate = options?.sampleRate ?? 1;
460
+ return {
461
+ module: _ZuckerTracingModule,
462
+ global: true,
463
+ providers: [
464
+ {
465
+ provide: InMemoryTracer,
466
+ useFactory: /* @__PURE__ */ __name(() => {
467
+ const sampler = new ParentBasedSampler(new RatioSampler(sampleRate));
468
+ return new InMemoryTracer(sampler);
469
+ }, "useFactory")
470
+ },
471
+ {
472
+ provide: import_core.APP_INTERCEPTOR,
473
+ useClass: TraceInterceptor
474
+ }
475
+ ],
476
+ exports: [
477
+ InMemoryTracer
478
+ ]
479
+ };
480
+ }
481
+ };
482
+ ZuckerTracingModule = _ts_decorate4([
483
+ (0, import_common4.Global)(),
484
+ (0, import_common4.Module)({})
485
+ ], ZuckerTracingModule);
486
+ // Annotate the CommonJS export names for ESM import in node:
487
+ 0 && (module.exports = {
488
+ AlwaysOffSampler,
489
+ AlwaysOnSampler,
490
+ InMemoryTracer,
491
+ ParentBasedSampler,
492
+ RatioSampler,
493
+ SamplingDecision,
494
+ Span,
495
+ SpanBuilder,
496
+ TraceInterceptor,
497
+ TraceMiddleware,
498
+ ZuckerTracingModule,
499
+ generateTraceparent,
500
+ getSpanId,
501
+ getTraceContext,
502
+ getTraceId,
503
+ runWithTraceContext
504
+ });