@sploov/trace 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/LICENSE +21 -0
- package/README.md +124 -0
- package/dist/core/Trace.d.ts +22 -0
- package/dist/core/Trace.d.ts.map +1 -0
- package/dist/core/Trace.js +122 -0
- package/dist/core/Trace.js.map +1 -0
- package/dist/example.d.ts +2 -0
- package/dist/example.d.ts.map +1 -0
- package/dist/example.js +34 -0
- package/dist/example.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/Express.d.ts +6 -0
- package/dist/integrations/Express.d.ts.map +1 -0
- package/dist/integrations/Express.js +21 -0
- package/dist/integrations/Express.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +54 -0
- package/dist/test.js.map +1 -0
- package/dist/types.d.ts +29 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/Formatter.d.ts +7 -0
- package/dist/ui/Formatter.d.ts.map +1 -0
- package/dist/ui/Formatter.js +61 -0
- package/dist/ui/Formatter.js.map +1 -0
- package/package.json +49 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sploov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# 🔍 Trace
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@sploov/trace)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
|
|
7
|
+
> **Execution flow, made visible.**
|
|
8
|
+
|
|
9
|
+
Trace is a lightweight, high-performance tracing and flow-visualization library for Node.js. Designed for developers who need to understand exactly what happens inside their async operations without the overhead of a full APM.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## ✨ Features
|
|
14
|
+
|
|
15
|
+
- 🚀 **Zero-Overhead Async Tracking**: Leverages `AsyncLocalStorage` for bulletproof context propagation.
|
|
16
|
+
- 🎨 **Visual Timelines**: Beautiful, colorized CLI output showing parallel and sequential execution.
|
|
17
|
+
- 🌲 **Nested Spans**: Effortlessly track parent-child relationships across complex async flows.
|
|
18
|
+
- 🛠 **Dev-Friendly**: Capture errors, add tags, and wrap functions with minimal boilerplate.
|
|
19
|
+
- 🔌 **Framework Ready**: First-class support for Express.js and easy integration for others.
|
|
20
|
+
- 💾 **Persistence**: Export traces to JSON/NDJSON and reload them for offline analysis.
|
|
21
|
+
- 🧩 **Extensible**: Hooks for custom exporters, metrics, and plugin support.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 📦 Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @sploov/trace
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 🚀 Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { trace } from '@sploov/trace';
|
|
37
|
+
|
|
38
|
+
async function performTask() {
|
|
39
|
+
await trace.startSpan('compute-data', async () => {
|
|
40
|
+
trace.tag('id', '12345');
|
|
41
|
+
|
|
42
|
+
// Simulate some work
|
|
43
|
+
await new Promise(r => setTimeout(r, 100));
|
|
44
|
+
|
|
45
|
+
console.log(trace.format());
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### The Output
|
|
51
|
+
|
|
52
|
+
Trace transforms complex async logs into an intuitive timeline:
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
● Trace ID: 87c2940c
|
|
56
|
+
● Duration: 105ms
|
|
57
|
+
● Spans: 1
|
|
58
|
+
|
|
59
|
+
Timeline Execution Flow
|
|
60
|
+
┌────────────────────────────────────────┐
|
|
61
|
+
│━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━│ compute-data 105ms [id:12345]
|
|
62
|
+
└────────────────────────────────────────┘
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 🛠 Advanced Usage
|
|
68
|
+
|
|
69
|
+
### Function Wrapping
|
|
70
|
+
Don't want to change your function body? Wrap it!
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const processUser = trace.wrap('process-user', (user) => {
|
|
74
|
+
// Automatically traced!
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Express Integration
|
|
79
|
+
Track entire request lifecycles with one line of code.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { expressTrace } from '@sploov/trace/middleware';
|
|
83
|
+
|
|
84
|
+
app.use(expressTrace);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Automatic Error Capture
|
|
88
|
+
Trace automatically catches errors and labels them in the timeline.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
await trace.startSpan('risky-operation', async () => {
|
|
92
|
+
throw new Error('Something went wrong');
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## ⚙️ Configuration
|
|
99
|
+
|
|
100
|
+
Tune Trace to fit your production needs:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
trace.configure({
|
|
104
|
+
enabled: true, // Toggle tracing globally
|
|
105
|
+
samplingRate: 0.05, // Only trace 5% of requests
|
|
106
|
+
maxSpans: 1000 // Protect memory in massive operations
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 🤝 Contributing
|
|
113
|
+
|
|
114
|
+
We love contributions! Check out our [Contributing Guide](CONTRIBUTING.md) to get started.
|
|
115
|
+
|
|
116
|
+
## 📄 License
|
|
117
|
+
|
|
118
|
+
Trace is [MIT Licensed](LICENSE).
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
<p align="center">
|
|
123
|
+
Built with ❤️ for the developer community.
|
|
124
|
+
</p>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SpanData, TraceData, TraceConfig, TracePlugin } from '../types.js';
|
|
2
|
+
export declare class TraceContext {
|
|
3
|
+
private storage;
|
|
4
|
+
private rootSpan;
|
|
5
|
+
private traceId;
|
|
6
|
+
private config;
|
|
7
|
+
private spanCount;
|
|
8
|
+
private isSampled;
|
|
9
|
+
private plugins;
|
|
10
|
+
configure(config: TraceConfig): void;
|
|
11
|
+
use(plugin: TracePlugin): void;
|
|
12
|
+
startSpan<T>(label: string, fn: (span: SpanData) => T): T;
|
|
13
|
+
wrap<Args extends any[], Ret>(label: string, fn: (...args: Args) => Ret): (...args: Args) => Ret;
|
|
14
|
+
tag(key: string, value: string | number | boolean): void;
|
|
15
|
+
get currentSpan(): SpanData | undefined;
|
|
16
|
+
private endSpan;
|
|
17
|
+
toJSON(): TraceData;
|
|
18
|
+
save(path: string): Promise<void>;
|
|
19
|
+
load(path: string): Promise<void>;
|
|
20
|
+
format(traceData?: TraceData): string;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=Trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Trace.d.ts","sourceRoot":"","sources":["../../src/core/Trace.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAGjF,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,QAAQ,CAAyB;IACzC,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,MAAM,CAIZ;IACF,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,OAAO,CAAqB;IAEpC,SAAS,CAAC,MAAM,EAAE,WAAW;IAI7B,GAAG,CAAC,MAAM,EAAE,WAAW;IAIvB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,CAAC,GAAG,CAAC;IA2DzD,IAAI,CAAC,IAAI,SAAS,GAAG,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,GAAG,GAAG,CAAC,GAAG,IAAI,EAAE,IAAI,KAAK,GAAG;IAIhG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI;IAKxD,IAAI,WAAW,IAAI,QAAQ,GAAG,SAAS,CAEtC;IAED,OAAO,CAAC,OAAO;IAYf,MAAM,IAAI,SAAS;IASb,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASvC,MAAM,CAAC,SAAS,GAAE,SAAyB,GAAG,MAAM;CAGrD"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { writeFile, readFile } from 'node:fs/promises';
|
|
4
|
+
import { Formatter } from '../ui/Formatter.js';
|
|
5
|
+
export class TraceContext {
|
|
6
|
+
storage = new AsyncLocalStorage();
|
|
7
|
+
rootSpan = null;
|
|
8
|
+
traceId = randomUUID().substring(0, 8);
|
|
9
|
+
config = {
|
|
10
|
+
enabled: process.env['TRACE_ENABLED'] !== 'false',
|
|
11
|
+
samplingRate: 1.0,
|
|
12
|
+
maxSpans: 1000
|
|
13
|
+
};
|
|
14
|
+
spanCount = 0;
|
|
15
|
+
isSampled = true;
|
|
16
|
+
plugins = [];
|
|
17
|
+
configure(config) {
|
|
18
|
+
this.config = { ...this.config, ...config };
|
|
19
|
+
}
|
|
20
|
+
use(plugin) {
|
|
21
|
+
this.plugins.push(plugin);
|
|
22
|
+
}
|
|
23
|
+
startSpan(label, fn) {
|
|
24
|
+
if (!this.config.enabled)
|
|
25
|
+
return fn({});
|
|
26
|
+
const parentSpan = this.storage.getStore();
|
|
27
|
+
if (!parentSpan) {
|
|
28
|
+
this.isSampled = Math.random() < this.config.samplingRate;
|
|
29
|
+
this.spanCount = 0;
|
|
30
|
+
this.rootSpan = null;
|
|
31
|
+
this.traceId = randomUUID().substring(0, 8);
|
|
32
|
+
if (this.isSampled) {
|
|
33
|
+
this.plugins.forEach(p => p.onTraceStart?.(this.traceId));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!this.isSampled)
|
|
37
|
+
return fn({});
|
|
38
|
+
if (this.spanCount >= this.config.maxSpans) {
|
|
39
|
+
return fn({});
|
|
40
|
+
}
|
|
41
|
+
this.spanCount++;
|
|
42
|
+
const span = {
|
|
43
|
+
id: randomUUID().substring(0, 8),
|
|
44
|
+
parentId: parentSpan ? parentSpan.id : null,
|
|
45
|
+
label,
|
|
46
|
+
startTime: Date.now(),
|
|
47
|
+
tags: {},
|
|
48
|
+
children: []
|
|
49
|
+
};
|
|
50
|
+
if (parentSpan) {
|
|
51
|
+
parentSpan.children.push(span);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.rootSpan = span;
|
|
55
|
+
}
|
|
56
|
+
this.plugins.forEach(p => p.onSpanStart?.(span));
|
|
57
|
+
return this.storage.run(span, () => {
|
|
58
|
+
try {
|
|
59
|
+
const result = fn(span);
|
|
60
|
+
if (result instanceof Promise) {
|
|
61
|
+
return result
|
|
62
|
+
.catch(err => {
|
|
63
|
+
span.error = err;
|
|
64
|
+
throw err;
|
|
65
|
+
})
|
|
66
|
+
.finally(() => this.endSpan(span));
|
|
67
|
+
}
|
|
68
|
+
this.endSpan(span);
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
span.error = error;
|
|
73
|
+
this.endSpan(span);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
wrap(label, fn) {
|
|
79
|
+
return (...args) => this.startSpan(label, () => fn(...args));
|
|
80
|
+
}
|
|
81
|
+
tag(key, value) {
|
|
82
|
+
const span = this.storage.getStore();
|
|
83
|
+
if (span)
|
|
84
|
+
span.tags[key] = value;
|
|
85
|
+
}
|
|
86
|
+
get currentSpan() {
|
|
87
|
+
return this.storage.getStore();
|
|
88
|
+
}
|
|
89
|
+
endSpan(span) {
|
|
90
|
+
if (span.id && span.endTime === undefined) {
|
|
91
|
+
span.endTime = Date.now();
|
|
92
|
+
span.duration = span.endTime - span.startTime;
|
|
93
|
+
this.plugins.forEach(p => p.onSpanEnd?.(span));
|
|
94
|
+
if (span === this.rootSpan) {
|
|
95
|
+
this.plugins.forEach(p => p.onTraceEnd?.(this.toJSON()));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
toJSON() {
|
|
100
|
+
return {
|
|
101
|
+
id: this.traceId,
|
|
102
|
+
rootSpan: this.rootSpan,
|
|
103
|
+
isSampled: this.isSampled,
|
|
104
|
+
spanCount: this.spanCount
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
async save(path) {
|
|
108
|
+
await writeFile(path, JSON.stringify(this.toJSON(), null, 2));
|
|
109
|
+
}
|
|
110
|
+
async load(path) {
|
|
111
|
+
const data = await readFile(path, 'utf-8');
|
|
112
|
+
const parsed = JSON.parse(data);
|
|
113
|
+
this.traceId = parsed.id;
|
|
114
|
+
this.rootSpan = parsed.rootSpan;
|
|
115
|
+
this.isSampled = parsed.isSampled ?? true;
|
|
116
|
+
this.spanCount = parsed.spanCount ?? 0;
|
|
117
|
+
}
|
|
118
|
+
format(traceData = this.toJSON()) {
|
|
119
|
+
return Formatter.format(traceData);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=Trace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Trace.js","sourceRoot":"","sources":["../../src/core/Trace.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":""}
|
package/dist/example.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { trace } from './index.js';
|
|
2
|
+
async function simulate() {
|
|
3
|
+
await trace.startSpan('root-request', async () => {
|
|
4
|
+
trace.tag('api_version', 'v1');
|
|
5
|
+
await trace.startSpan('authentication', async () => {
|
|
6
|
+
await new Promise(r => setTimeout(r, 20));
|
|
7
|
+
trace.tag('user_id', 'user_123');
|
|
8
|
+
});
|
|
9
|
+
await Promise.all([
|
|
10
|
+
trace.startSpan('fetch-db', async () => {
|
|
11
|
+
await new Promise(r => setTimeout(r, 60));
|
|
12
|
+
trace.tag('db.query', 'SELECT * FROM users');
|
|
13
|
+
}),
|
|
14
|
+
trace.startSpan('fetch-cache', async () => {
|
|
15
|
+
await new Promise(r => setTimeout(r, 15));
|
|
16
|
+
})
|
|
17
|
+
]);
|
|
18
|
+
try {
|
|
19
|
+
await trace.startSpan('legacy-service', async () => {
|
|
20
|
+
await new Promise(r => setTimeout(r, 10));
|
|
21
|
+
throw new Error('Service Unavailable');
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
// handled
|
|
26
|
+
}
|
|
27
|
+
await trace.startSpan('serialization', async () => {
|
|
28
|
+
await new Promise(r => setTimeout(r, 5));
|
|
29
|
+
});
|
|
30
|
+
console.log(trace.format());
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
simulate();
|
|
34
|
+
//# sourceMappingURL=example.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"example.js","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,KAAK,UAAU,QAAQ;IACrB,MAAM,KAAK,CAAC,SAAS,CAAC,cAAc,EAAE,KAAK,IAAI,EAAE;QAC/C,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAE/B,MAAM,KAAK,CAAC,SAAS,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC1C,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,GAAG,CAAC;YAChB,KAAK,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;gBACrC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC1C,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;YAC/C,CAAC,CAAC;YACF,KAAK,CAAC,SAAS,CAAC,aAAa,EAAE,KAAK,IAAI,EAAE;gBACxC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC5C,CAAC,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,SAAS,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;gBACjD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;gBAC1C,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;YACzC,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,UAAU;QACZ,CAAC;QAED,MAAM,KAAK,CAAC,SAAS,CAAC,eAAe,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,EAAE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TraceContext } from './core/Trace.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './core/Trace.js';
|
|
4
|
+
export * from './ui/Formatter.js';
|
|
5
|
+
export * from './integrations/Express.js';
|
|
6
|
+
export declare const trace: TraceContext;
|
|
7
|
+
export default trace;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAE1C,eAAO,MAAM,KAAK,cAAqB,CAAC;AACxC,eAAe,KAAK,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TraceContext } from './core/Trace.js';
|
|
2
|
+
export * from './types.js';
|
|
3
|
+
export * from './core/Trace.js';
|
|
4
|
+
export * from './ui/Formatter.js';
|
|
5
|
+
export * from './integrations/Express.js';
|
|
6
|
+
export const trace = new TraceContext();
|
|
7
|
+
export default trace;
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAE1C,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,YAAY,EAAE,CAAC;AACxC,eAAe,KAAK,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Express.d.ts","sourceRoot":"","sources":["../../src/integrations/Express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAG/D;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,QAmB3E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { trace } from '../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Express middleware for Trace
|
|
4
|
+
*/
|
|
5
|
+
export function expressTrace(req, res, next) {
|
|
6
|
+
const label = `${req.method} ${req.path}`;
|
|
7
|
+
trace.startSpan(label, async () => {
|
|
8
|
+
trace.tag('http.method', req.method);
|
|
9
|
+
trace.tag('http.path', req.path);
|
|
10
|
+
const responseFinished = new Promise((resolve) => {
|
|
11
|
+
res.on('finish', () => {
|
|
12
|
+
trace.tag('http.status', res.statusCode);
|
|
13
|
+
resolve();
|
|
14
|
+
});
|
|
15
|
+
res.on('close', resolve);
|
|
16
|
+
});
|
|
17
|
+
next();
|
|
18
|
+
await responseFinished;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=Express.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Express.js","sourceRoot":"","sources":["../../src/integrations/Express.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEpC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IAC1E,MAAM,KAAK,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;IAE1C,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QAChC,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QACrC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAEjC,MAAM,gBAAgB,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACrD,GAAG,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;gBACpB,KAAK,CAAC,GAAG,CAAC,aAAa,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBACzC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;YACH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC;QAEP,MAAM,gBAAgB,CAAC;IACzB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/test.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":""}
|
package/dist/test.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { trace } from './index.js';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
async function testBasicTracing() {
|
|
4
|
+
console.log('Testing basic tracing...');
|
|
5
|
+
await trace.startSpan('root', async () => {
|
|
6
|
+
trace.tag('test', 'basic');
|
|
7
|
+
await trace.startSpan('child', async () => {
|
|
8
|
+
await new Promise(r => setTimeout(r, 10));
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
const data = trace.toJSON();
|
|
12
|
+
assert.strictEqual(data.rootSpan?.label, 'root');
|
|
13
|
+
assert.strictEqual(data.rootSpan?.children.length, 1);
|
|
14
|
+
assert.strictEqual(data.rootSpan?.children[0]?.label, 'child');
|
|
15
|
+
assert.strictEqual(data.rootSpan?.tags['test'], 'basic');
|
|
16
|
+
console.log('✅ Basic tracing passed');
|
|
17
|
+
}
|
|
18
|
+
async function testErrorCapturing() {
|
|
19
|
+
console.log('Testing error capturing...');
|
|
20
|
+
try {
|
|
21
|
+
await trace.startSpan('error-parent', async () => {
|
|
22
|
+
throw new Error('boom');
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (e) { }
|
|
26
|
+
const data = trace.toJSON();
|
|
27
|
+
assert.strictEqual(data.rootSpan?.label, 'error-parent');
|
|
28
|
+
assert.strictEqual(data.rootSpan?.error?.message, 'boom');
|
|
29
|
+
console.log('✅ Error capturing passed');
|
|
30
|
+
}
|
|
31
|
+
async function testSampling() {
|
|
32
|
+
console.log('Testing sampling...');
|
|
33
|
+
trace.configure({ samplingRate: 0 });
|
|
34
|
+
await trace.startSpan('not-sampled', () => { });
|
|
35
|
+
assert.strictEqual(trace.format(), '\u001b[33m⚠ Trace not sampled.\u001b[39m');
|
|
36
|
+
trace.configure({ samplingRate: 1 });
|
|
37
|
+
await trace.startSpan('sampled', () => { });
|
|
38
|
+
assert.notStrictEqual(trace.format(), '\u001b[33m⚠ Trace not sampled.\u001b[39m');
|
|
39
|
+
console.log('✅ Sampling passed');
|
|
40
|
+
}
|
|
41
|
+
async function runTests() {
|
|
42
|
+
try {
|
|
43
|
+
await testBasicTracing();
|
|
44
|
+
await testErrorCapturing();
|
|
45
|
+
await testSampling();
|
|
46
|
+
console.log('\nAll tests passed! 🚀');
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
console.error('❌ Test failed:', error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
runTests();
|
|
54
|
+
//# sourceMappingURL=test.js.map
|
package/dist/test.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface SpanData {
|
|
2
|
+
id: string;
|
|
3
|
+
parentId: string | null;
|
|
4
|
+
label: string;
|
|
5
|
+
startTime: number;
|
|
6
|
+
endTime?: number;
|
|
7
|
+
duration?: number;
|
|
8
|
+
error?: any;
|
|
9
|
+
tags: Record<string, string | number | boolean>;
|
|
10
|
+
children: SpanData[];
|
|
11
|
+
}
|
|
12
|
+
export interface TraceData {
|
|
13
|
+
id: string;
|
|
14
|
+
rootSpan: SpanData | null;
|
|
15
|
+
isSampled: boolean;
|
|
16
|
+
spanCount: number;
|
|
17
|
+
}
|
|
18
|
+
export interface TraceConfig {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
samplingRate?: number;
|
|
21
|
+
maxSpans?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface TracePlugin {
|
|
24
|
+
onTraceStart?: (id: string) => void;
|
|
25
|
+
onTraceEnd?: (data: TraceData) => void;
|
|
26
|
+
onSpanStart?: (span: SpanData) => void;
|
|
27
|
+
onSpanEnd?: (span: SpanData) => void;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAChD,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC;IACvC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;CACtC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Formatter.d.ts","sourceRoot":"","sources":["../../src/ui/Formatter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAY,MAAM,aAAa,CAAC;AAEvD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAM;IAEvC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM;IA8B3C,OAAO,CAAC,MAAM,CAAC,UAAU;CA0C1B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
export class Formatter {
|
|
3
|
+
static BAR_WIDTH = 40;
|
|
4
|
+
static format(traceData) {
|
|
5
|
+
if (!traceData.isSampled)
|
|
6
|
+
return pc.yellow('⚠ Trace not sampled.');
|
|
7
|
+
const { id, rootSpan, spanCount } = traceData;
|
|
8
|
+
if (!rootSpan)
|
|
9
|
+
return pc.red('✖ No trace recorded.');
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
const totalDuration = (rootSpan.duration || (now - rootSpan.startTime)) || 1;
|
|
12
|
+
const rootStart = rootSpan.startTime;
|
|
13
|
+
let output = `
|
|
14
|
+
${pc.bold(pc.cyan('●'))} ${pc.bold('Trace ID:')} ${pc.gray(id)}
|
|
15
|
+
`;
|
|
16
|
+
output += ` ${pc.bold(pc.cyan('●'))} ${pc.bold('Duration:')} ${pc.blue(`${totalDuration}ms`)}
|
|
17
|
+
`;
|
|
18
|
+
output += ` ${pc.bold(pc.cyan('●'))} ${pc.bold('Spans: ')} ${pc.gray(spanCount.toString())}
|
|
19
|
+
|
|
20
|
+
`;
|
|
21
|
+
const barHeader = ' '.repeat(2) + pc.dim('Timeline') + ' '.repeat(this.BAR_WIDTH - 8 + 2) + pc.dim('Execution Flow') + '\n';
|
|
22
|
+
output += barHeader;
|
|
23
|
+
output += ` ${pc.gray('┌' + '─'.repeat(this.BAR_WIDTH) + '┐')}\n`;
|
|
24
|
+
output += this.renderSpan(rootSpan, 0, rootStart, totalDuration);
|
|
25
|
+
output += ` ${pc.gray('└' + '─'.repeat(this.BAR_WIDTH) + '┘')}\n`;
|
|
26
|
+
return output;
|
|
27
|
+
}
|
|
28
|
+
static renderSpan(span, depth, rootStart, totalDuration) {
|
|
29
|
+
const now = Date.now();
|
|
30
|
+
const relativeStart = span.startTime - rootStart;
|
|
31
|
+
const duration = span.duration || (now - span.startTime);
|
|
32
|
+
const startPos = Math.max(0, Math.floor((relativeStart / totalDuration) * this.BAR_WIDTH));
|
|
33
|
+
let barWidth = Math.max(1, Math.floor((duration / totalDuration) * this.BAR_WIDTH));
|
|
34
|
+
// Ensure bar doesn't exceed total width
|
|
35
|
+
if (startPos + barWidth > this.BAR_WIDTH) {
|
|
36
|
+
barWidth = this.BAR_WIDTH - startPos;
|
|
37
|
+
}
|
|
38
|
+
const isError = !!span.error;
|
|
39
|
+
const barColor = isError ? pc.red : (depth === 0 ? pc.cyan : (depth % 2 === 0 ? pc.green : pc.yellow));
|
|
40
|
+
const barContent = '━'.repeat(Math.max(0, barWidth));
|
|
41
|
+
const bar = ' '.repeat(startPos) + barColor(barContent);
|
|
42
|
+
const paddedBar = bar.padEnd(this.BAR_WIDTH + (isError ? 10 : 10)); // ANSI compensation
|
|
43
|
+
const indent = depth > 0 ? pc.gray(' '.repeat(depth - 1) + '└─ ') : '';
|
|
44
|
+
const label = isError ? pc.red(pc.bold(span.label)) : (depth === 0 ? pc.bold(pc.white(span.label)) : pc.white(span.label));
|
|
45
|
+
const durationStr = pc.dim(`${duration}ms`);
|
|
46
|
+
const tags = Object.keys(span.tags).length
|
|
47
|
+
? ` ${pc.dim(`[${Object.entries(span.tags).map(([k, v]) => `${pc.cyan(k)}:${v}`).join(', ')}]`)}`
|
|
48
|
+
: '';
|
|
49
|
+
const errorMsg = span.error ? ` ${pc.bgRed(pc.white(pc.bold(' ERROR ')))} ${pc.red(span.error.message || span.error)}` : '';
|
|
50
|
+
// We need to carefully handle the padding because of ANSI codes
|
|
51
|
+
// Re-calculating visible length for the bar part
|
|
52
|
+
const visibleBar = ' '.repeat(startPos) + '━'.repeat(Math.max(0, barWidth));
|
|
53
|
+
const barPadding = ' '.repeat(this.BAR_WIDTH - visibleBar.length);
|
|
54
|
+
let line = ` ${pc.gray('│')}${bar}${barPadding}${pc.gray('│')} ${indent}${label} ${durationStr}${tags}${errorMsg}\n`;
|
|
55
|
+
for (const child of span.children) {
|
|
56
|
+
line += this.renderSpan(child, depth + 1, rootStart, totalDuration);
|
|
57
|
+
}
|
|
58
|
+
return line;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=Formatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Formatter.js","sourceRoot":"","sources":["../../src/ui/Formatter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sploov/trace",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Trace is a lightweight execution tracing and flow-visualization tool that helps developers understand how code actually runs across time, state, and events.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./dist/index.js",
|
|
9
|
+
"./middleware": "./dist/integrations/Express.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/sploov/Trace.git"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"trace",
|
|
23
|
+
"tracing",
|
|
24
|
+
"telemetry",
|
|
25
|
+
"visualization",
|
|
26
|
+
"async-safe",
|
|
27
|
+
"performance",
|
|
28
|
+
"node",
|
|
29
|
+
"typescript"
|
|
30
|
+
],
|
|
31
|
+
"author": "Sploov",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"type": "module",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/sploov/Trace/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/sploov/Trace#readme",
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/express": "^5.0.6",
|
|
40
|
+
"@types/node": "^25.0.3",
|
|
41
|
+
"ts-node": "^10.9.2",
|
|
42
|
+
"tsx": "^4.21.0",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"express": "^5.2.1",
|
|
47
|
+
"picocolors": "^1.1.1"
|
|
48
|
+
}
|
|
49
|
+
}
|