chronos-tracer 0.1.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.
Files changed (42) hide show
  1. package/dist/config/chronos.config.d.ts +6 -0
  2. package/dist/config/chronos.config.js +15 -0
  3. package/dist/config/chronos.config.js.map +1 -0
  4. package/dist/context/trace-context.d.ts +21 -0
  5. package/dist/context/trace-context.js +39 -0
  6. package/dist/context/trace-context.js.map +1 -0
  7. package/dist/decorators/trace.decorator.d.ts +1 -0
  8. package/dist/decorators/trace.decorator.js +63 -0
  9. package/dist/decorators/trace.decorator.js.map +1 -0
  10. package/dist/emitter/emitter.d.ts +2 -0
  11. package/dist/emitter/emitter.js +20 -0
  12. package/dist/emitter/emitter.js.map +1 -0
  13. package/dist/error/error.d.ts +2 -0
  14. package/dist/error/error.js +84 -0
  15. package/dist/error/error.js.map +1 -0
  16. package/dist/index.d.ts +4 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/interceptor/trace.interceptor.d.ts +0 -0
  20. package/dist/interceptor/trace.interceptor.js +2 -0
  21. package/dist/interceptor/trace.interceptor.js.map +1 -0
  22. package/dist/middleware/trace.middleware.d.ts +1 -0
  23. package/dist/middleware/trace.middleware.js +45 -0
  24. package/dist/middleware/trace.middleware.js.map +1 -0
  25. package/dist/span/span.d.ts +1 -0
  26. package/dist/span/span.js +55 -0
  27. package/dist/span/span.js.map +1 -0
  28. package/dist/types/event.d.ts +18 -0
  29. package/dist/types/event.js +3 -0
  30. package/dist/types/event.js.map +1 -0
  31. package/index.js +1 -0
  32. package/package.json +20 -0
  33. package/src/config/chronos.config.ts +19 -0
  34. package/src/context/trace-context.ts +50 -0
  35. package/src/decorators/trace.decorator.ts +73 -0
  36. package/src/emitter/emitter.ts +18 -0
  37. package/src/error/error.ts +95 -0
  38. package/src/index.ts +5 -0
  39. package/src/middleware/trace.middleware.ts +49 -0
  40. package/src/span/span.ts +62 -0
  41. package/src/types/event.ts +25 -0
  42. package/tsconfig.json +13 -0
@@ -0,0 +1,6 @@
1
+ export type ChronosConfig = {
2
+ endpoint: string;
3
+ service: string;
4
+ };
5
+ export declare function initChronos(userConfig: ChronosConfig): void;
6
+ export declare function getChronosConfig(): ChronosConfig;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initChronos = initChronos;
4
+ exports.getChronosConfig = getChronosConfig;
5
+ let config = null;
6
+ function initChronos(userConfig) {
7
+ config = userConfig;
8
+ }
9
+ function getChronosConfig() {
10
+ if (!config) {
11
+ throw new Error("Chronos is not initialized. Call initChronos() in your app startup.");
12
+ }
13
+ return config;
14
+ }
15
+ //# sourceMappingURL=chronos.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chronos.config.js","sourceRoot":"","sources":["../../src/config/chronos.config.ts"],"names":[],"mappings":";;AAOA,kCAEC;AAED,4CAOC;AAbD,IAAI,MAAM,GAAyB,IAAI,CAAC;AAExC,SAAgB,WAAW,CAAC,UAAyB;IACnD,MAAM,GAAG,UAAU,CAAC;AACtB,CAAC;AAED,SAAgB,gBAAgB;IAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,21 @@
1
+ export type TraceStore = {
2
+ traceId: string;
3
+ rootSpanId: string;
4
+ service: string;
5
+ spanStack: string[];
6
+ stage?: string;
7
+ errorEmitted?: boolean;
8
+ };
9
+ declare class TraceContextClass {
10
+ private als;
11
+ run(store: Omit<TraceStore, "spanStack">, fn: () => void): void;
12
+ /** Get current store */
13
+ getStore(): TraceStore | undefined;
14
+ setStage(stage: string): void;
15
+ /** Span helpers */
16
+ getCurrentSpan(): string | undefined;
17
+ pushSpan(spanId: string): void;
18
+ popSpan(): void;
19
+ }
20
+ export declare const TraceContext: TraceContextClass;
21
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TraceContext = void 0;
4
+ const async_hooks_1 = require("async_hooks");
5
+ class TraceContextClass {
6
+ constructor() {
7
+ this.als = new async_hooks_1.AsyncLocalStorage();
8
+ }
9
+ run(store, fn) {
10
+ this.als.run({ ...store, spanStack: [store.rootSpanId] }, fn);
11
+ }
12
+ /** Get current store */
13
+ getStore() {
14
+ return this.als.getStore();
15
+ }
16
+ setStage(stage) {
17
+ const ctx = this.als.getStore();
18
+ if (ctx) {
19
+ ctx.stage = stage;
20
+ }
21
+ }
22
+ /** Span helpers */
23
+ getCurrentSpan() {
24
+ const store = this.getStore();
25
+ return store?.spanStack[store.spanStack.length - 1];
26
+ }
27
+ pushSpan(spanId) {
28
+ const store = this.getStore();
29
+ if (store)
30
+ store.spanStack.push(spanId);
31
+ }
32
+ popSpan() {
33
+ const store = this.getStore();
34
+ if (store)
35
+ store.spanStack.pop();
36
+ }
37
+ }
38
+ exports.TraceContext = new TraceContextClass();
39
+ //# sourceMappingURL=trace-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-context.js","sourceRoot":"","sources":["../../src/context/trace-context.ts"],"names":[],"mappings":";;;AAAA,6CAAgD;AAYhD,MAAM,iBAAiB;IAAvB;QACU,QAAG,GAAG,IAAI,+BAAiB,EAAc,CAAC;IAkCpD,CAAC;IAhCC,GAAG,CAAC,KAAoC,EAAE,EAAc;QACtD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAChE,CAAC;IAGD,wBAAwB;IACxB,QAAQ;QACN,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED,QAAQ,CAAC,KAAa;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC;QACpB,CAAC;IACH,CAAC;IAED,mBAAmB;IACnB,cAAc;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,QAAQ,CAAC,MAAc;QACrB,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK;YAAE,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO;QACL,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC9B,IAAI,KAAK;YAAE,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC;IACnC,CAAC;CACF;AAEY,QAAA,YAAY,GAAG,IAAI,iBAAiB,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function Trace(operation?: string): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Trace = Trace;
4
+ const crypto_1 = require("crypto");
5
+ const trace_context_1 = require("../context/trace-context");
6
+ const emitter_1 = require("../emitter/emitter");
7
+ function Trace(operation) {
8
+ return function (target, propertyKey, descriptor) {
9
+ const original = descriptor.value;
10
+ descriptor.value = async function (...args) {
11
+ const store = trace_context_1.TraceContext.getStore();
12
+ // 🔴 If no trace context, DO NOTHING
13
+ if (!store) {
14
+ return original.apply(this, args);
15
+ }
16
+ const spanId = (0, crypto_1.randomUUID)();
17
+ const parentSpanId = trace_context_1.TraceContext.getCurrentSpan();
18
+ const opName = operation ?? `${target.constructor.name}.${propertyKey}`;
19
+ trace_context_1.TraceContext.pushSpan(spanId);
20
+ trace_context_1.TraceContext.setStage(opName);
21
+ (0, emitter_1.emit)({
22
+ trace_id: store.traceId,
23
+ span_id: spanId,
24
+ parent_span_id: parentSpanId,
25
+ service: store.service,
26
+ operation: opName,
27
+ type: 'start',
28
+ timestamp: Date.now().toString(),
29
+ });
30
+ try {
31
+ const result = await original.apply(this, args);
32
+ (0, emitter_1.emit)({
33
+ trace_id: store.traceId,
34
+ span_id: spanId,
35
+ parent_span_id: parentSpanId,
36
+ service: store.service,
37
+ operation: opName,
38
+ type: 'end',
39
+ timestamp: Date.now().toString(),
40
+ });
41
+ return result;
42
+ }
43
+ catch (err) {
44
+ (0, emitter_1.emit)({
45
+ trace_id: store.traceId,
46
+ span_id: spanId,
47
+ parent_span_id: parentSpanId,
48
+ service: store.service,
49
+ operation: opName,
50
+ type: 'error',
51
+ error_message: err?.message ?? 'Unknown error',
52
+ status_code: err?.status,
53
+ timestamp: Date.now().toString(),
54
+ });
55
+ throw err;
56
+ }
57
+ finally {
58
+ trace_context_1.TraceContext.popSpan();
59
+ }
60
+ };
61
+ };
62
+ }
63
+ //# sourceMappingURL=trace.decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.decorator.js","sourceRoot":"","sources":["../../src/decorators/trace.decorator.ts"],"names":[],"mappings":";;AAIA,sBAoEC;AAxED,mCAAoC;AACpC,4DAAwD;AACxD,gDAA0C;AAE1C,SAAgB,KAAK,CAAC,SAAkB;IACtC,OAAO,UACL,MAAW,EACX,WAAmB,EACnB,UAA8B;QAE9B,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC;QAElC,UAAU,CAAC,KAAK,GAAG,KAAK,WAAW,GAAG,IAAW;YAC/C,MAAM,KAAK,GAAG,4BAAY,CAAC,QAAQ,EAAE,CAAC;YAEtC,qCAAqC;YACrC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YACpC,CAAC;YAED,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,4BAAY,CAAC,cAAc,EAAE,CAAC;YACnD,MAAM,MAAM,GACV,SAAS,IAAI,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;YAE3D,4BAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAE9B,4BAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAE9B,IAAA,cAAI,EAAC;gBACH,QAAQ,EAAE,KAAK,CAAC,OAAO;gBACvB,OAAO,EAAE,MAAM;gBACf,cAAc,EAAE,YAAY;gBAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,SAAS,EAAE,MAAM;gBACjB,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;aACjC,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAEhD,IAAA,cAAI,EAAC;oBACH,QAAQ,EAAE,KAAK,CAAC,OAAO;oBACvB,OAAO,EAAE,MAAM;oBACf,cAAc,EAAE,YAAY;oBAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,MAAM;oBACjB,IAAI,EAAE,KAAK;oBACX,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;iBACjC,CAAC,CAAC;gBAEH,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAQ,EAAE,CAAC;gBAClB,IAAA,cAAI,EAAC;oBACH,QAAQ,EAAE,KAAK,CAAC,OAAO;oBACvB,OAAO,EAAE,MAAM;oBACf,cAAc,EAAE,YAAY;oBAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,SAAS,EAAE,MAAM;oBACjB,IAAI,EAAE,OAAO;oBACb,aAAa,EAAE,GAAG,EAAE,OAAO,IAAI,eAAe;oBAC9C,WAAW,EAAE,GAAG,EAAE,MAAM;oBACxB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;iBACjC,CAAC,CAAC;gBAEH,MAAM,GAAG,CAAC;YACZ,CAAC;oBAAS,CAAC;gBACT,4BAAY,CAAC,OAAO,EAAE,CAAC;YACzB,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { TraceEvent } from "../types/event";
2
+ export declare function emit(event: TraceEvent): Promise<void>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emit = emit;
4
+ const chronos_config_1 = require("../config/chronos.config");
5
+ async function emit(event) {
6
+ const { endpoint } = (0, chronos_config_1.getChronosConfig)();
7
+ try {
8
+ await fetch(endpoint, {
9
+ method: "POST",
10
+ headers: {
11
+ "Content-Type": "application/json"
12
+ },
13
+ body: JSON.stringify(event)
14
+ });
15
+ }
16
+ catch (err) {
17
+ console.error("Failed to emit trace event:", err);
18
+ }
19
+ }
20
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/emitter/emitter.ts"],"names":[],"mappings":";;AAIA,oBAaC;AAjBD,6DAA4D;AAIrD,KAAK,UAAU,IAAI,CAAC,KAAiB;IACxC,MAAM,EAAC,QAAQ,EAAC,GAAG,IAAA,iCAAgB,GAAE,CAAA;IACrC,IAAG,CAAC;QACA,MAAM,KAAK,CAAC,QAAQ,EAAE;YAClB,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACL,cAAc,EAAE,kBAAkB;aACrC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;SAC9B,CAAC,CAAC;IACP,CAAC;IAAA,OAAM,GAAG,EAAC,CAAC;QACR,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IACtD,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function errorFunction(error: unknown): void;
2
+ export declare function extractMeaningfulMessage(error: unknown): string;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errorFunction = errorFunction;
4
+ exports.extractMeaningfulMessage = extractMeaningfulMessage;
5
+ const trace_context_1 = require("../context/trace-context");
6
+ const emitter_1 = require("../emitter/emitter");
7
+ // export function errorFunction(error: unknown) {
8
+ // console.log("Logging error trace event", error);
9
+ // const ctx = TraceContext.getStore();
10
+ // emit({
11
+ // trace_id: ctx?.traceId || "unknown",
12
+ // span_id: ctx?.rootSpanId || "unknown",
13
+ // service: ctx?.service || "unknown",
14
+ // operation: `${ctx?.stage || "unknown"}`,
15
+ // type: "start",
16
+ // timestamp: new Date().toISOString(),
17
+ // failedAt: ctx?.stage ?? "UNKNOWN",
18
+ // errorType: error?.constructor?.name ?? "UnknownError",
19
+ // error_message:
20
+ // typeof error === "object" &&
21
+ // error !== null &&
22
+ // "getResponse" in error &&
23
+ // typeof (error as any).getResponse === "function"
24
+ // ? (() => {
25
+ // const r = (error as any).getResponse();
26
+ // if (Array.isArray(r?.message)) return r.message.join(", ");
27
+ // if (typeof r?.message === "string") return r.message;
28
+ // return error instanceof Error ? error.message : String(error);
29
+ // })()
30
+ // : error instanceof Error
31
+ // ? error.message
32
+ // : String(error),
33
+ // });
34
+ // }
35
+ function errorFunction(error) {
36
+ const ctx = trace_context_1.TraceContext.getStore();
37
+ console.log("Error trace event", error);
38
+ // 🚫 If already emitted once, do NOT emit again
39
+ if (ctx?.errorEmitted)
40
+ return;
41
+ if (ctx)
42
+ ctx.errorEmitted = true;
43
+ trace_context_1.TraceContext.setStage('ERROR');
44
+ (0, emitter_1.emit)({
45
+ trace_id: ctx?.traceId || "unknown",
46
+ span_id: ctx?.rootSpanId || "unknown",
47
+ service: ctx?.service || "unknown",
48
+ operation: `${ctx?.stage || "unknown"}`,
49
+ type: "error",
50
+ timestamp: new Date().toISOString(),
51
+ failedAt: ctx?.stage ?? "UNKNOWN",
52
+ errorType: error?.constructor?.name ?? "UnknownError",
53
+ error_message: extractMeaningfulMessage(error),
54
+ });
55
+ }
56
+ function extractMeaningfulMessage(error) {
57
+ // NestJS / HttpException-like errors (ValidationPipe, Guards, etc.)
58
+ if (typeof error === 'object' &&
59
+ error !== null &&
60
+ 'getResponse' in error &&
61
+ typeof error.getResponse === 'function') {
62
+ try {
63
+ const response = error.getResponse();
64
+ // ValidationPipe: message is usually an array
65
+ if (Array.isArray(response?.message)) {
66
+ return response.message.join(', ');
67
+ }
68
+ // Other HttpExceptions
69
+ if (typeof response?.message === 'string') {
70
+ return response.message;
71
+ }
72
+ }
73
+ catch {
74
+ // fall through to generic handling
75
+ }
76
+ }
77
+ // Standard JS Error
78
+ if (error instanceof Error) {
79
+ return error.message;
80
+ }
81
+ // Fallback
82
+ return String(error);
83
+ }
84
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/error/error.ts"],"names":[],"mappings":";;AAkCA,sCAuBC;AAGD,4DAgCC;AA5FD,4DAAwD;AACxD,gDAA0C;AAE1C,kDAAkD;AAClD,qDAAqD;AACrD,yCAAyC;AAEzC,WAAW;AACX,2CAA2C;AAC3C,6CAA6C;AAC7C,0CAA0C;AAC1C,+CAA+C;AAC/C,qBAAqB;AACrB,2CAA2C;AAC3C,yCAAyC;AACzC,6DAA6D;AAC7D,qBAAqB;AACrB,qCAAqC;AACrC,0BAA0B;AAC1B,kCAAkC;AAClC,yDAAyD;AACzD,qBAAqB;AACrB,sDAAsD;AACtD,0EAA0E;AAC1E,oEAAoE;AACpE,6EAA6E;AAC7E,iBAAiB;AACjB,mCAAmC;AACnC,4BAA4B;AAC5B,6BAA6B;AAC7B,QAAQ;AACR,IAAI;AAGJ,SAAgB,aAAa,CAAC,KAAc;IAC1C,MAAM,GAAG,GAAG,4BAAY,CAAC,QAAQ,EAAE,CAAC;IAEpC,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IAExC,gDAAgD;IAChD,IAAI,GAAG,EAAE,YAAY;QAAE,OAAO;IAE9B,IAAI,GAAG;QAAE,GAAG,CAAC,YAAY,GAAG,IAAI,CAAC;IAEjC,4BAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE/B,IAAA,cAAI,EAAC;QACH,QAAQ,EAAE,GAAG,EAAE,OAAO,IAAI,SAAS;QACnC,OAAO,EAAE,GAAG,EAAE,UAAU,IAAI,SAAS;QACrC,OAAO,EAAE,GAAG,EAAE,OAAO,IAAI,SAAS;QAClC,SAAS,EAAE,GAAG,GAAG,EAAE,KAAK,IAAI,SAAS,EAAE;QACvC,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,QAAQ,EAAE,GAAG,EAAE,KAAK,IAAI,SAAS;QACjC,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,IAAI,cAAc;QACrD,aAAa,EAAE,wBAAwB,CAAC,KAAK,CAAC;KAC/C,CAAC,CAAC;AACL,CAAC;AAGD,SAAgB,wBAAwB,CAAC,KAAc;IACrD,oEAAoE;IACpE,IACE,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,aAAa,IAAI,KAAK;QACtB,OAAQ,KAAa,CAAC,WAAW,KAAK,UAAU,EAChD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAI,KAAa,CAAC,WAAW,EAAE,CAAC;YAE9C,8CAA8C;YAC9C,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;gBACrC,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;YAED,uBAAuB;YACvB,IAAI,OAAO,QAAQ,EAAE,OAAO,KAAK,QAAQ,EAAE,CAAC;gBAC1C,OAAO,QAAQ,CAAC,OAAO,CAAC;YAC1B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;QACrC,CAAC;IACH,CAAC;IAED,oBAAoB;IACpB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IAED,WAAW;IACX,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Trace } from "./decorators/trace.decorator";
2
+ export { traceMiddleware } from "./middleware/trace.middleware";
3
+ export { errorFunction } from "./error/error";
4
+ export { initChronos } from "./config/chronos.config";
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initChronos = exports.errorFunction = exports.traceMiddleware = exports.Trace = void 0;
4
+ var trace_decorator_1 = require("./decorators/trace.decorator");
5
+ Object.defineProperty(exports, "Trace", { enumerable: true, get: function () { return trace_decorator_1.Trace; } });
6
+ var trace_middleware_1 = require("./middleware/trace.middleware");
7
+ Object.defineProperty(exports, "traceMiddleware", { enumerable: true, get: function () { return trace_middleware_1.traceMiddleware; } });
8
+ var error_1 = require("./error/error");
9
+ Object.defineProperty(exports, "errorFunction", { enumerable: true, get: function () { return error_1.errorFunction; } });
10
+ var chronos_config_1 = require("./config/chronos.config");
11
+ Object.defineProperty(exports, "initChronos", { enumerable: true, get: function () { return chronos_config_1.initChronos; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,gEAAqD;AAA5C,wGAAA,KAAK,OAAA;AACd,kEAAgE;AAAvD,mHAAA,eAAe,OAAA;AACxB,uCAA8C;AAArC,sGAAA,aAAa,OAAA;AACtB,0DAAsD;AAA7C,6GAAA,WAAW,OAAA"}
File without changes
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ //# sourceMappingURL=trace.interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.interceptor.js","sourceRoot":"","sources":["../../src/interceptor/trace.interceptor.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ export declare function traceMiddleware(req: any, res: any, next: () => void): void;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.traceMiddleware = traceMiddleware;
4
+ // middleware/trace-middleware.ts
5
+ const crypto_1 = require("crypto");
6
+ const trace_context_1 = require("../context/trace-context");
7
+ const emitter_1 = require("../emitter/emitter");
8
+ function traceMiddleware(req, res, next) {
9
+ const traceId = (0, crypto_1.randomUUID)();
10
+ const rootSpanId = (0, crypto_1.randomUUID)();
11
+ const store = {
12
+ traceId,
13
+ service: 'payment-service',
14
+ spanStack: [rootSpanId],
15
+ };
16
+ trace_context_1.TraceContext.run({
17
+ traceId,
18
+ rootSpanId,
19
+ service: 'payment-service',
20
+ }, () => {
21
+ trace_context_1.TraceContext.setStage('REQUEST');
22
+ (0, emitter_1.emit)({
23
+ trace_id: traceId,
24
+ span_id: rootSpanId,
25
+ service: store.service,
26
+ operation: `${req.method} ${req.url}`,
27
+ type: 'start',
28
+ timestamp: new Date().toISOString(),
29
+ });
30
+ res.on('finish', () => {
31
+ (0, emitter_1.emit)({
32
+ trace_id: traceId,
33
+ span_id: rootSpanId,
34
+ service: store.service,
35
+ operation: `${req.method} ${req.url}`,
36
+ type: 'end',
37
+ status_code: res.statusCode,
38
+ success: res.statusCode < 500,
39
+ timestamp: new Date().toISOString(),
40
+ });
41
+ });
42
+ next();
43
+ });
44
+ }
45
+ //# sourceMappingURL=trace.middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace.middleware.js","sourceRoot":"","sources":["../../src/middleware/trace.middleware.ts"],"names":[],"mappings":";;AAKA,0CA2CC;AAhDD,iCAAiC;AACjC,mCAAoC;AACpC,4DAAwD;AACxD,gDAA0C;AAE1C,SAAgB,eAAe,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAgB;IAClE,MAAM,OAAO,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC7B,MAAM,UAAU,GAAG,IAAA,mBAAU,GAAE,CAAC;IAEhC,MAAM,KAAK,GAAG;QACZ,OAAO;QACP,OAAO,EAAE,iBAAiB;QAC1B,SAAS,EAAE,CAAC,UAAU,CAAC;KACxB,CAAC;IAEF,4BAAY,CAAC,GAAG,CAAC;QACX,OAAO;QACP,UAAU;QACV,OAAO,EAAE,iBAAiB;KAC3B,EAAE,GAAG,EAAE;QAGV,4BAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAEjC,IAAA,cAAI,EAAC;YACH,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,UAAU;YACnB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE;YACrC,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;YACpB,IAAA,cAAI,EAAC;gBACH,QAAQ,EAAE,OAAO;gBACjB,OAAO,EAAE,UAAU;gBACnB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE;gBACrC,IAAI,EAAE,KAAK;gBACX,WAAW,EAAE,GAAG,CAAC,UAAU;gBAC3B,OAAO,EAAE,GAAG,CAAC,UAAU,GAAG,GAAG;gBAC7B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;IACT,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function runSpan<T>(operation: string, fn: () => Promise<T>, payload?: any): Promise<T>;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runSpan = runSpan;
4
+ const crypto_1 = require("crypto");
5
+ const emitter_1 = require("../emitter/emitter");
6
+ const trace_context_1 = require("../context/trace-context");
7
+ async function runSpan(operation, fn, payload) {
8
+ const { getCurrentSpan, pushSpan, popSpan } = trace_context_1.TraceContext;
9
+ const store = trace_context_1.TraceContext.getStore();
10
+ if (!store)
11
+ return fn();
12
+ const spanId = (0, crypto_1.randomUUID)();
13
+ const parentSpanId = getCurrentSpan();
14
+ pushSpan(spanId);
15
+ await (0, emitter_1.emit)({
16
+ trace_id: store.traceId,
17
+ span_id: spanId,
18
+ parent_span_id: parentSpanId,
19
+ service: store.service,
20
+ operation,
21
+ type: "start",
22
+ timestamp: new Date().toISOString(),
23
+ payload: payload ? { actual: payload } : undefined,
24
+ });
25
+ try {
26
+ const result = await fn();
27
+ await (0, emitter_1.emit)({
28
+ trace_id: store.traceId,
29
+ span_id: spanId,
30
+ parent_span_id: parentSpanId,
31
+ service: store.service,
32
+ operation,
33
+ type: "end",
34
+ timestamp: new Date().toISOString(),
35
+ });
36
+ return result;
37
+ }
38
+ catch (err) {
39
+ await (0, emitter_1.emit)({
40
+ trace_id: store.traceId,
41
+ span_id: spanId,
42
+ parent_span_id: parentSpanId,
43
+ service: store.service,
44
+ operation,
45
+ type: "error",
46
+ timestamp: new Date().toISOString(),
47
+ error_message: err?.message ?? "Unknown error",
48
+ });
49
+ throw err;
50
+ }
51
+ finally {
52
+ popSpan();
53
+ }
54
+ }
55
+ //# sourceMappingURL=span.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"span.js","sourceRoot":"","sources":["../../src/span/span.ts"],"names":[],"mappings":";;AAMA,0BAuDC;AA7DD,mCAAoC;AACpC,gDAA0C;AAC1C,4DAEkC;AAE3B,KAAK,UAAU,OAAO,CAC3B,SAAiB,EACjB,EAAoB,EACpB,OAAa;IAEb,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,4BAAY,CAAC;IAC3D,MAAM,KAAK,GAAG,4BAAY,CAAC,QAAQ,EAAE,CAAC;IACtC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,EAAE,CAAC;IAExB,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,YAAY,GAAG,cAAc,EAAE,CAAC;IAEtC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEjB,MAAM,IAAA,cAAI,EAAC;QACT,QAAQ,EAAE,KAAK,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM;QACf,cAAc,EAAE,YAAY;QAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,SAAS;QACT,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS;KACnD,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;QAE1B,MAAM,IAAA,cAAI,EAAC;YACT,QAAQ,EAAE,KAAK,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,YAAY;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS;YACT,IAAI,EAAE,KAAK;YACX,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAA,cAAI,EAAC;YACT,QAAQ,EAAE,KAAK,CAAC,OAAO;YACvB,OAAO,EAAE,MAAM;YACf,cAAc,EAAE,YAAY;YAC5B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS;YACT,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,aAAa,EAAE,GAAG,EAAE,OAAO,IAAI,eAAe;SAC/C,CAAC,CAAC;QAEH,MAAM,GAAG,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface TraceEvent {
2
+ trace_id: string;
3
+ span_id: string;
4
+ parent_span_id?: string;
5
+ service: string;
6
+ operation: string;
7
+ type: "start" | "end" | "error";
8
+ timestamp: string;
9
+ status_code?: number;
10
+ success?: boolean;
11
+ error_message?: string;
12
+ errorType?: string;
13
+ failedAt?: string;
14
+ payload?: {
15
+ actual?: any;
16
+ expected?: any;
17
+ };
18
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/types/event.ts"],"names":[],"mappings":""}
package/index.js ADDED
@@ -0,0 +1 @@
1
+ import fs from "fs"
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "chronos-tracer",
3
+ "version": "0.1.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1",
8
+ "build": "tsc",
9
+ "prepublishOnly": "npm run build"
10
+ },
11
+ "keywords": ["tracing", "chronos", "decorator"],
12
+ "author": "Yousuf Khan",
13
+ "license": "MIT",
14
+ "description": "package for tracing using decorators",
15
+ "devDependencies": {
16
+ "esm": "^3.2.25",
17
+ "ts-node": "^10.9.2",
18
+ "typescript": "^5.9.3"
19
+ }
20
+ }
@@ -0,0 +1,19 @@
1
+ export type ChronosConfig = {
2
+ endpoint: string;
3
+ service: string;
4
+ };
5
+
6
+ let config: ChronosConfig | null = null;
7
+
8
+ export function initChronos(userConfig: ChronosConfig) {
9
+ config = userConfig;
10
+ }
11
+
12
+ export function getChronosConfig(): ChronosConfig {
13
+ if (!config) {
14
+ throw new Error(
15
+ "Chronos is not initialized. Call initChronos() in your app startup."
16
+ );
17
+ }
18
+ return config;
19
+ }
@@ -0,0 +1,50 @@
1
+ import { AsyncLocalStorage } from "async_hooks";
2
+ import { randomUUID } from "crypto";
3
+
4
+ export type TraceStore = {
5
+ traceId: string;
6
+ rootSpanId: string;
7
+ service: string;
8
+ spanStack: string[];
9
+ stage?: string;
10
+ errorEmitted?: boolean;
11
+ };
12
+
13
+ class TraceContextClass {
14
+ private als = new AsyncLocalStorage<TraceStore>();
15
+
16
+ run(store: Omit<TraceStore, "spanStack">, fn: () => void) {
17
+ this.als.run({ ...store, spanStack: [store.rootSpanId] }, fn);
18
+ }
19
+
20
+
21
+ /** Get current store */
22
+ getStore(): TraceStore | undefined {
23
+ return this.als.getStore();
24
+ }
25
+
26
+ setStage(stage: string) {
27
+ const ctx = this.als.getStore();
28
+ if (ctx) {
29
+ ctx.stage = stage;
30
+ }
31
+ }
32
+
33
+ /** Span helpers */
34
+ getCurrentSpan(): string | undefined {
35
+ const store = this.getStore();
36
+ return store?.spanStack[store.spanStack.length - 1];
37
+ }
38
+
39
+ pushSpan(spanId: string) {
40
+ const store = this.getStore();
41
+ if (store) store.spanStack.push(spanId);
42
+ }
43
+
44
+ popSpan() {
45
+ const store = this.getStore();
46
+ if (store) store.spanStack.pop();
47
+ }
48
+ }
49
+
50
+ export const TraceContext = new TraceContextClass();
@@ -0,0 +1,73 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { TraceContext } from '../context/trace-context';
3
+ import { emit } from '../emitter/emitter';
4
+
5
+ export function Trace(operation?: string) {
6
+ return function (
7
+ target: any,
8
+ propertyKey: string,
9
+ descriptor: PropertyDescriptor
10
+ ) {
11
+ const original = descriptor.value;
12
+
13
+ descriptor.value = async function (...args: any[]) {
14
+ const store = TraceContext.getStore();
15
+
16
+ // 🔴 If no trace context, DO NOTHING
17
+ if (!store) {
18
+ return original.apply(this, args);
19
+ }
20
+
21
+ const spanId = randomUUID();
22
+ const parentSpanId = TraceContext.getCurrentSpan();
23
+ const opName =
24
+ operation ?? `${target.constructor.name}.${propertyKey}`;
25
+
26
+ TraceContext.pushSpan(spanId);
27
+
28
+ TraceContext.setStage(opName);
29
+
30
+ emit({
31
+ trace_id: store.traceId,
32
+ span_id: spanId,
33
+ parent_span_id: parentSpanId,
34
+ service: store.service,
35
+ operation: opName,
36
+ type: 'start',
37
+ timestamp: Date.now().toString(),
38
+ });
39
+
40
+ try {
41
+ const result = await original.apply(this, args);
42
+
43
+ emit({
44
+ trace_id: store.traceId,
45
+ span_id: spanId,
46
+ parent_span_id: parentSpanId,
47
+ service: store.service,
48
+ operation: opName,
49
+ type: 'end',
50
+ timestamp: Date.now().toString(),
51
+ });
52
+
53
+ return result;
54
+ } catch (err: any) {
55
+ emit({
56
+ trace_id: store.traceId,
57
+ span_id: spanId,
58
+ parent_span_id: parentSpanId,
59
+ service: store.service,
60
+ operation: opName,
61
+ type: 'error',
62
+ error_message: err?.message ?? 'Unknown error',
63
+ status_code: err?.status,
64
+ timestamp: Date.now().toString(),
65
+ });
66
+
67
+ throw err;
68
+ } finally {
69
+ TraceContext.popSpan();
70
+ }
71
+ };
72
+ };
73
+ }
@@ -0,0 +1,18 @@
1
+ import { getChronosConfig } from "../config/chronos.config";
2
+ import { TraceEvent } from "../types/event";
3
+
4
+
5
+ export async function emit(event: TraceEvent){
6
+ const {endpoint} = getChronosConfig()
7
+ try{
8
+ await fetch(endpoint, {
9
+ method: "POST",
10
+ headers: {
11
+ "Content-Type": "application/json"
12
+ },
13
+ body: JSON.stringify(event)
14
+ });
15
+ }catch(err){
16
+ console.error("Failed to emit trace event:", err);
17
+ }
18
+ }
@@ -0,0 +1,95 @@
1
+ import { TraceContext } from "../context/trace-context";
2
+ import { emit } from "../emitter/emitter";
3
+
4
+ // export function errorFunction(error: unknown) {
5
+ // console.log("Logging error trace event", error);
6
+ // const ctx = TraceContext.getStore();
7
+
8
+ // emit({
9
+ // trace_id: ctx?.traceId || "unknown",
10
+ // span_id: ctx?.rootSpanId || "unknown",
11
+ // service: ctx?.service || "unknown",
12
+ // operation: `${ctx?.stage || "unknown"}`,
13
+ // type: "start",
14
+ // timestamp: new Date().toISOString(),
15
+ // failedAt: ctx?.stage ?? "UNKNOWN",
16
+ // errorType: error?.constructor?.name ?? "UnknownError",
17
+ // error_message:
18
+ // typeof error === "object" &&
19
+ // error !== null &&
20
+ // "getResponse" in error &&
21
+ // typeof (error as any).getResponse === "function"
22
+ // ? (() => {
23
+ // const r = (error as any).getResponse();
24
+ // if (Array.isArray(r?.message)) return r.message.join(", ");
25
+ // if (typeof r?.message === "string") return r.message;
26
+ // return error instanceof Error ? error.message : String(error);
27
+ // })()
28
+ // : error instanceof Error
29
+ // ? error.message
30
+ // : String(error),
31
+ // });
32
+ // }
33
+
34
+
35
+ export function errorFunction(error: unknown) {
36
+ const ctx = TraceContext.getStore();
37
+
38
+ console.log("Error trace event", error);
39
+
40
+ // 🚫 If already emitted once, do NOT emit again
41
+ if (ctx?.errorEmitted) return;
42
+
43
+ if (ctx) ctx.errorEmitted = true;
44
+
45
+ TraceContext.setStage('ERROR');
46
+
47
+ emit({
48
+ trace_id: ctx?.traceId || "unknown",
49
+ span_id: ctx?.rootSpanId || "unknown",
50
+ service: ctx?.service || "unknown",
51
+ operation: `${ctx?.stage || "unknown"}`,
52
+ type: "error",
53
+ timestamp: new Date().toISOString(),
54
+ failedAt: ctx?.stage ?? "UNKNOWN",
55
+ errorType: error?.constructor?.name ?? "UnknownError",
56
+ error_message: extractMeaningfulMessage(error),
57
+ });
58
+ }
59
+
60
+
61
+ export function extractMeaningfulMessage(error: unknown): string {
62
+ // NestJS / HttpException-like errors (ValidationPipe, Guards, etc.)
63
+ if (
64
+ typeof error === 'object' &&
65
+ error !== null &&
66
+ 'getResponse' in error &&
67
+ typeof (error as any).getResponse === 'function'
68
+ ) {
69
+ try {
70
+ const response = (error as any).getResponse();
71
+
72
+ // ValidationPipe: message is usually an array
73
+ if (Array.isArray(response?.message)) {
74
+ return response.message.join(', ');
75
+ }
76
+
77
+ // Other HttpExceptions
78
+ if (typeof response?.message === 'string') {
79
+ return response.message;
80
+ }
81
+ } catch {
82
+ // fall through to generic handling
83
+ }
84
+ }
85
+
86
+ // Standard JS Error
87
+ if (error instanceof Error) {
88
+ return error.message;
89
+ }
90
+
91
+ // Fallback
92
+ return String(error);
93
+ }
94
+
95
+
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+
2
+ export { Trace } from "./decorators/trace.decorator";
3
+ export { traceMiddleware } from "./middleware/trace.middleware";
4
+ export { errorFunction } from "./error/error";
5
+ export { initChronos } from "./config/chronos.config";
@@ -0,0 +1,49 @@
1
+ // middleware/trace-middleware.ts
2
+ import { randomUUID } from 'crypto';
3
+ import { TraceContext } from '../context/trace-context';
4
+ import { emit } from '../emitter/emitter';
5
+
6
+ export function traceMiddleware(req: any, res: any, next: () => void) {
7
+ const traceId = randomUUID();
8
+ const rootSpanId = randomUUID();
9
+
10
+ const store = {
11
+ traceId,
12
+ service: 'payment-service',
13
+ spanStack: [rootSpanId],
14
+ };
15
+
16
+ TraceContext.run({
17
+ traceId,
18
+ rootSpanId,
19
+ service: 'payment-service',
20
+ }, () => {
21
+
22
+
23
+ TraceContext.setStage('REQUEST');
24
+
25
+ emit({
26
+ trace_id: traceId,
27
+ span_id: rootSpanId,
28
+ service: store.service,
29
+ operation: `${req.method} ${req.url}`,
30
+ type: 'start',
31
+ timestamp: new Date().toISOString(),
32
+ });
33
+
34
+ res.on('finish', () => {
35
+ emit({
36
+ trace_id: traceId,
37
+ span_id: rootSpanId,
38
+ service: store.service,
39
+ operation: `${req.method} ${req.url}`,
40
+ type: 'end',
41
+ status_code: res.statusCode,
42
+ success: res.statusCode < 500,
43
+ timestamp: new Date().toISOString(),
44
+ });
45
+ });
46
+
47
+ next();
48
+ });
49
+ }
@@ -0,0 +1,62 @@
1
+ import { randomUUID } from "crypto";
2
+ import { emit } from "../emitter/emitter";
3
+ import {
4
+ TraceContext
5
+ } from "../context/trace-context";
6
+
7
+ export async function runSpan<T>(
8
+ operation: string,
9
+ fn: () => Promise<T>,
10
+ payload?: any
11
+ ): Promise<T> {
12
+ const { getCurrentSpan, pushSpan, popSpan } = TraceContext;
13
+ const store = TraceContext.getStore();
14
+ if (!store) return fn();
15
+
16
+ const spanId = randomUUID();
17
+ const parentSpanId = getCurrentSpan();
18
+
19
+ pushSpan(spanId);
20
+
21
+ await emit({
22
+ trace_id: store.traceId,
23
+ span_id: spanId,
24
+ parent_span_id: parentSpanId,
25
+ service: store.service,
26
+ operation,
27
+ type: "start",
28
+ timestamp: new Date().toISOString(),
29
+ payload: payload ? { actual: payload } : undefined,
30
+ });
31
+
32
+ try {
33
+ const result = await fn();
34
+
35
+ await emit({
36
+ trace_id: store.traceId,
37
+ span_id: spanId,
38
+ parent_span_id: parentSpanId,
39
+ service: store.service,
40
+ operation,
41
+ type: "end",
42
+ timestamp: new Date().toISOString(),
43
+ });
44
+
45
+ return result;
46
+ } catch (err: any) {
47
+ await emit({
48
+ trace_id: store.traceId,
49
+ span_id: spanId,
50
+ parent_span_id: parentSpanId,
51
+ service: store.service,
52
+ operation,
53
+ type: "error",
54
+ timestamp: new Date().toISOString(),
55
+ error_message: err?.message ?? "Unknown error",
56
+ });
57
+
58
+ throw err;
59
+ } finally {
60
+ popSpan();
61
+ }
62
+ }
@@ -0,0 +1,25 @@
1
+ export interface TraceEvent {
2
+ trace_id: string;
3
+ span_id: string;
4
+ parent_span_id?: string;
5
+
6
+ service: string;
7
+ operation: string;
8
+
9
+ type: "start" | "end" | "error";
10
+
11
+ timestamp: string;
12
+
13
+ status_code?: number;
14
+ success?: boolean;
15
+
16
+ error_message?: string;
17
+
18
+ errorType?: string;
19
+ failedAt?: string;
20
+
21
+ payload?: {
22
+ actual?: any;
23
+ expected?: any;
24
+ };
25
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "moduleResolution": "node",
5
+ "declaration": true,
6
+ "target": "es2020",
7
+ "sourceMap": true,
8
+ "outDir": "dist",
9
+ "strict": true,
10
+ "esModuleInterop": true
11
+ },
12
+ "include": ["src/**/*"]
13
+ }