@tracewayapp/backend 0.2.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/README.md +368 -0
- package/dist/index.d.mts +232 -0
- package/dist/index.d.ts +232 -0
- package/dist/index.js +742 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +681 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
CollectionFrameStore: () => CollectionFrameStore,
|
|
34
|
+
addSpanToContext: () => addSpanToContext,
|
|
35
|
+
captureCurrentTrace: () => captureCurrentTrace,
|
|
36
|
+
captureException: () => captureException,
|
|
37
|
+
captureExceptionWithAttributes: () => captureExceptionWithAttributes,
|
|
38
|
+
captureMessage: () => captureMessage,
|
|
39
|
+
captureMetric: () => captureMetric,
|
|
40
|
+
captureTask: () => captureTask,
|
|
41
|
+
captureTrace: () => captureTrace,
|
|
42
|
+
collectMetrics: () => collectMetrics,
|
|
43
|
+
endSpan: () => endSpan,
|
|
44
|
+
forkTraceContext: () => forkTraceContext,
|
|
45
|
+
formatErrorStackTrace: () => formatErrorStackTrace,
|
|
46
|
+
getTraceContext: () => getTraceContext,
|
|
47
|
+
getTraceDuration: () => getTraceDuration,
|
|
48
|
+
getTraceId: () => getTraceId,
|
|
49
|
+
getTraceSpans: () => getTraceSpans,
|
|
50
|
+
hasTraceContext: () => hasTraceContext,
|
|
51
|
+
init: () => init,
|
|
52
|
+
measureTask: () => measureTask,
|
|
53
|
+
resetCpuTracking: () => resetCpuTracking,
|
|
54
|
+
runWithTraceContext: () => runWithTraceContext,
|
|
55
|
+
setTraceAttribute: () => setTraceAttribute,
|
|
56
|
+
setTraceAttributes: () => setTraceAttributes,
|
|
57
|
+
setTraceResponseInfo: () => setTraceResponseInfo,
|
|
58
|
+
shouldSample: () => shouldSample,
|
|
59
|
+
shutdown: () => shutdown,
|
|
60
|
+
startSpan: () => startSpan,
|
|
61
|
+
traceContextStorage: () => asyncLocalStorage,
|
|
62
|
+
withTraceContext: () => withTraceContext
|
|
63
|
+
});
|
|
64
|
+
module.exports = __toCommonJS(index_exports);
|
|
65
|
+
|
|
66
|
+
// src/traceway.ts
|
|
67
|
+
var import_core4 = require("@tracewayapp/core");
|
|
68
|
+
|
|
69
|
+
// src/collection-frame-store.ts
|
|
70
|
+
var zlib = __toESM(require("zlib"));
|
|
71
|
+
|
|
72
|
+
// src/typed-ring.ts
|
|
73
|
+
var TypedRing = class {
|
|
74
|
+
arr;
|
|
75
|
+
head = 0;
|
|
76
|
+
_capacity;
|
|
77
|
+
_len = 0;
|
|
78
|
+
constructor(capacity) {
|
|
79
|
+
this._capacity = capacity;
|
|
80
|
+
this.arr = new Array(capacity).fill(null);
|
|
81
|
+
}
|
|
82
|
+
get length() {
|
|
83
|
+
return this._len;
|
|
84
|
+
}
|
|
85
|
+
get capacity() {
|
|
86
|
+
return this._capacity;
|
|
87
|
+
}
|
|
88
|
+
push(val) {
|
|
89
|
+
this.arr[this.head] = val;
|
|
90
|
+
this.head = (this.head + 1) % this._capacity;
|
|
91
|
+
if (this._len < this._capacity) {
|
|
92
|
+
this._len += 1;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
readAll() {
|
|
96
|
+
const result = [];
|
|
97
|
+
for (let i = 0; i < this._len; i++) {
|
|
98
|
+
const idx = (this.head - this._len + i + this._capacity) % this._capacity;
|
|
99
|
+
result.push(this.arr[idx]);
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
clear() {
|
|
104
|
+
for (let i = 0; i < this.arr.length; i++) {
|
|
105
|
+
this.arr[i] = null;
|
|
106
|
+
}
|
|
107
|
+
this.head = 0;
|
|
108
|
+
this._len = 0;
|
|
109
|
+
}
|
|
110
|
+
remove(vals) {
|
|
111
|
+
if (vals.length === 0) return 0;
|
|
112
|
+
const toRemove = new Set(vals);
|
|
113
|
+
let writeIdx = 0;
|
|
114
|
+
let removed = 0;
|
|
115
|
+
for (let i = 0; i < this._len; i++) {
|
|
116
|
+
const readIdx = (this.head - this._len + i + this._capacity) % this._capacity;
|
|
117
|
+
if (toRemove.has(this.arr[readIdx])) {
|
|
118
|
+
removed++;
|
|
119
|
+
} else {
|
|
120
|
+
if (writeIdx !== i) {
|
|
121
|
+
const destIdx = (this.head - this._len + writeIdx + this._capacity) % this._capacity;
|
|
122
|
+
this.arr[destIdx] = this.arr[readIdx];
|
|
123
|
+
}
|
|
124
|
+
writeIdx++;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (let i = writeIdx; i < this._len; i++) {
|
|
128
|
+
const idx = (this.head - this._len + i + this._capacity) % this._capacity;
|
|
129
|
+
this.arr[idx] = null;
|
|
130
|
+
}
|
|
131
|
+
this._len = writeIdx;
|
|
132
|
+
this.head = (this.head - removed + this._capacity) % this._capacity;
|
|
133
|
+
return removed;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// src/metrics.ts
|
|
138
|
+
var os = __toESM(require("os"));
|
|
139
|
+
var import_core = require("@tracewayapp/core");
|
|
140
|
+
var import_core2 = require("@tracewayapp/core");
|
|
141
|
+
var prevCpuUsage = null;
|
|
142
|
+
var prevCpuTime = null;
|
|
143
|
+
function collectMetrics() {
|
|
144
|
+
const metrics = [];
|
|
145
|
+
const recordedAt = (0, import_core2.nowISO)();
|
|
146
|
+
const mem = process.memoryUsage();
|
|
147
|
+
metrics.push({
|
|
148
|
+
name: import_core.METRIC_MEM_USED,
|
|
149
|
+
value: Math.round(mem.rss / 1024 / 1024 * 100) / 100,
|
|
150
|
+
recordedAt
|
|
151
|
+
});
|
|
152
|
+
const totalMem = os.totalmem();
|
|
153
|
+
metrics.push({
|
|
154
|
+
name: import_core.METRIC_MEM_TOTAL,
|
|
155
|
+
value: Math.round(totalMem / 1024 / 1024 * 100) / 100,
|
|
156
|
+
recordedAt
|
|
157
|
+
});
|
|
158
|
+
const currentCpuUsage = process.cpuUsage();
|
|
159
|
+
const currentTime = Date.now();
|
|
160
|
+
if (prevCpuUsage !== null && prevCpuTime !== null) {
|
|
161
|
+
const elapsedMs = currentTime - prevCpuTime;
|
|
162
|
+
if (elapsedMs > 0) {
|
|
163
|
+
const userDelta = currentCpuUsage.user - prevCpuUsage.user;
|
|
164
|
+
const systemDelta = currentCpuUsage.system - prevCpuUsage.system;
|
|
165
|
+
const totalDeltaMs = (userDelta + systemDelta) / 1e3;
|
|
166
|
+
const cpuCount = os.cpus().length || 1;
|
|
167
|
+
const cpuPercent = totalDeltaMs / elapsedMs / cpuCount * 100;
|
|
168
|
+
metrics.push({
|
|
169
|
+
name: import_core.METRIC_CPU_USED_PCNT,
|
|
170
|
+
value: Math.round(cpuPercent * 100) / 100,
|
|
171
|
+
recordedAt
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
prevCpuUsage = currentCpuUsage;
|
|
176
|
+
prevCpuTime = currentTime;
|
|
177
|
+
return metrics;
|
|
178
|
+
}
|
|
179
|
+
function resetCpuTracking() {
|
|
180
|
+
prevCpuUsage = null;
|
|
181
|
+
prevCpuTime = null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/collection-frame-store.ts
|
|
185
|
+
var CollectionFrameStore = class {
|
|
186
|
+
current = null;
|
|
187
|
+
currentSetAt = Date.now();
|
|
188
|
+
sendQueue;
|
|
189
|
+
lastUploadStarted = null;
|
|
190
|
+
collectionTimer = null;
|
|
191
|
+
metricsTimer = null;
|
|
192
|
+
apiUrl;
|
|
193
|
+
token;
|
|
194
|
+
debug;
|
|
195
|
+
collectionInterval;
|
|
196
|
+
uploadThrottle;
|
|
197
|
+
metricsInterval;
|
|
198
|
+
version;
|
|
199
|
+
serverName;
|
|
200
|
+
sampleRate;
|
|
201
|
+
errorSampleRate;
|
|
202
|
+
constructor(options) {
|
|
203
|
+
this.apiUrl = options.apiUrl;
|
|
204
|
+
this.token = options.token;
|
|
205
|
+
this.debug = options.debug;
|
|
206
|
+
this.collectionInterval = options.collectionInterval;
|
|
207
|
+
this.uploadThrottle = options.uploadThrottle;
|
|
208
|
+
this.metricsInterval = options.metricsInterval;
|
|
209
|
+
this.version = options.version;
|
|
210
|
+
this.serverName = options.serverName;
|
|
211
|
+
this.sampleRate = options.sampleRate;
|
|
212
|
+
this.errorSampleRate = options.errorSampleRate;
|
|
213
|
+
this.sendQueue = new TypedRing(
|
|
214
|
+
options.maxCollectionFrames
|
|
215
|
+
);
|
|
216
|
+
this.collectionTimer = setInterval(() => {
|
|
217
|
+
this.tick();
|
|
218
|
+
}, this.collectionInterval);
|
|
219
|
+
if (this.collectionTimer && typeof this.collectionTimer.unref === "function") {
|
|
220
|
+
this.collectionTimer.unref();
|
|
221
|
+
}
|
|
222
|
+
this.metricsTimer = setInterval(() => {
|
|
223
|
+
this.collectSystemMetrics();
|
|
224
|
+
}, this.metricsInterval);
|
|
225
|
+
if (this.metricsTimer && typeof this.metricsTimer.unref === "function") {
|
|
226
|
+
this.metricsTimer.unref();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
tick() {
|
|
230
|
+
if (this.current !== null) {
|
|
231
|
+
if (this.currentSetAt < Date.now() - this.collectionInterval) {
|
|
232
|
+
this.rotateCurrentCollectionFrame();
|
|
233
|
+
this.processSendQueue();
|
|
234
|
+
}
|
|
235
|
+
} else if (this.sendQueue.length > 0) {
|
|
236
|
+
this.processSendQueue();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
ensureCurrent() {
|
|
240
|
+
if (this.current === null) {
|
|
241
|
+
this.current = { stackTraces: [], metrics: [], traces: [] };
|
|
242
|
+
this.currentSetAt = Date.now();
|
|
243
|
+
}
|
|
244
|
+
return this.current;
|
|
245
|
+
}
|
|
246
|
+
addException(exception) {
|
|
247
|
+
this.ensureCurrent().stackTraces.push(exception);
|
|
248
|
+
}
|
|
249
|
+
addMetric(metric) {
|
|
250
|
+
this.ensureCurrent().metrics.push(metric);
|
|
251
|
+
}
|
|
252
|
+
addTrace(trace) {
|
|
253
|
+
this.ensureCurrent().traces.push(trace);
|
|
254
|
+
}
|
|
255
|
+
rotateCurrentCollectionFrame() {
|
|
256
|
+
if (this.current !== null) {
|
|
257
|
+
this.sendQueue.push(this.current);
|
|
258
|
+
this.current = null;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
processSendQueue() {
|
|
262
|
+
if (this.lastUploadStarted === null || this.lastUploadStarted < Date.now() - this.uploadThrottle) {
|
|
263
|
+
this.lastUploadStarted = Date.now();
|
|
264
|
+
const frames = this.sendQueue.readAll();
|
|
265
|
+
if (frames.length > 0) {
|
|
266
|
+
this.triggerUpload(frames);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
triggerUpload(framesToSend) {
|
|
271
|
+
const payload = {
|
|
272
|
+
collectionFrames: framesToSend,
|
|
273
|
+
appVersion: this.version,
|
|
274
|
+
serverName: this.serverName
|
|
275
|
+
};
|
|
276
|
+
let jsonData;
|
|
277
|
+
try {
|
|
278
|
+
jsonData = JSON.stringify(payload);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (this.debug) {
|
|
281
|
+
console.error("Traceway: failed to serialize frames:", err);
|
|
282
|
+
}
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
let gzipped;
|
|
286
|
+
try {
|
|
287
|
+
gzipped = new Uint8Array(zlib.gzipSync(Buffer.from(jsonData)));
|
|
288
|
+
} catch (err) {
|
|
289
|
+
if (this.debug) {
|
|
290
|
+
console.error("Traceway: failed to gzip:", err);
|
|
291
|
+
}
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
fetch(this.apiUrl, {
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: {
|
|
297
|
+
"Content-Type": "application/json",
|
|
298
|
+
"Content-Encoding": "gzip",
|
|
299
|
+
Authorization: `Bearer ${this.token}`
|
|
300
|
+
},
|
|
301
|
+
body: gzipped
|
|
302
|
+
}).then((resp) => {
|
|
303
|
+
if (resp.status === 200) {
|
|
304
|
+
this.sendQueue.remove(framesToSend);
|
|
305
|
+
} else if (this.debug) {
|
|
306
|
+
console.error(`Traceway: upload returned status ${resp.status}`);
|
|
307
|
+
}
|
|
308
|
+
}).catch((err) => {
|
|
309
|
+
if (this.debug) {
|
|
310
|
+
console.error("Traceway: upload failed:", err);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
collectSystemMetrics() {
|
|
315
|
+
try {
|
|
316
|
+
const metrics = collectMetrics();
|
|
317
|
+
for (const metric of metrics) {
|
|
318
|
+
this.addMetric(metric);
|
|
319
|
+
}
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (this.debug) {
|
|
322
|
+
console.error("Traceway: failed to collect metrics:", err);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async shutdown() {
|
|
327
|
+
if (this.collectionTimer !== null) {
|
|
328
|
+
clearInterval(this.collectionTimer);
|
|
329
|
+
this.collectionTimer = null;
|
|
330
|
+
}
|
|
331
|
+
if (this.metricsTimer !== null) {
|
|
332
|
+
clearInterval(this.metricsTimer);
|
|
333
|
+
this.metricsTimer = null;
|
|
334
|
+
}
|
|
335
|
+
this.rotateCurrentCollectionFrame();
|
|
336
|
+
const frames = this.sendQueue.readAll();
|
|
337
|
+
if (frames.length === 0) return;
|
|
338
|
+
const payload = {
|
|
339
|
+
collectionFrames: frames,
|
|
340
|
+
appVersion: this.version,
|
|
341
|
+
serverName: this.serverName
|
|
342
|
+
};
|
|
343
|
+
try {
|
|
344
|
+
const jsonData = JSON.stringify(payload);
|
|
345
|
+
const gzipped = new Uint8Array(zlib.gzipSync(Buffer.from(jsonData)));
|
|
346
|
+
const resp = await fetch(this.apiUrl, {
|
|
347
|
+
method: "POST",
|
|
348
|
+
headers: {
|
|
349
|
+
"Content-Type": "application/json",
|
|
350
|
+
"Content-Encoding": "gzip",
|
|
351
|
+
Authorization: `Bearer ${this.token}`
|
|
352
|
+
},
|
|
353
|
+
body: gzipped
|
|
354
|
+
});
|
|
355
|
+
if (resp.status === 200) {
|
|
356
|
+
this.sendQueue.remove(frames);
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
if (this.debug) {
|
|
360
|
+
console.error("Traceway: shutdown upload failed:", err);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// src/stack-trace.ts
|
|
367
|
+
function formatErrorStackTrace(error) {
|
|
368
|
+
const lines = [];
|
|
369
|
+
const typeName = error.constructor.name || "Error";
|
|
370
|
+
lines.push(`${typeName}: ${error.message}`);
|
|
371
|
+
if (error.stack) {
|
|
372
|
+
const stackLines = error.stack.split("\n");
|
|
373
|
+
for (const line of stackLines) {
|
|
374
|
+
const match = line.match(/^\s+at\s+(.+?)\s+\((.+):(\d+):\d+\)$/);
|
|
375
|
+
if (match) {
|
|
376
|
+
const funcName = shortenFunctionName(match[1]);
|
|
377
|
+
const file = shortenFilePath(match[2]);
|
|
378
|
+
const lineNum = match[3];
|
|
379
|
+
lines.push(`${funcName}()`);
|
|
380
|
+
lines.push(` ${file}:${lineNum}`);
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
const matchNoParens = line.match(/^\s+at\s+(.+):(\d+):\d+$/);
|
|
384
|
+
if (matchNoParens) {
|
|
385
|
+
const file = shortenFilePath(matchNoParens[1]);
|
|
386
|
+
const lineNum = matchNoParens[2];
|
|
387
|
+
lines.push(`<anonymous>()`);
|
|
388
|
+
lines.push(` ${file}:${lineNum}`);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
const matchFnOnly = line.match(/^\s+at\s+(.+)$/);
|
|
392
|
+
if (matchFnOnly) {
|
|
393
|
+
const funcName = shortenFunctionName(matchFnOnly[1]);
|
|
394
|
+
lines.push(`${funcName}()`);
|
|
395
|
+
lines.push(` <unknown>:0`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return lines.join("\n") + "\n";
|
|
400
|
+
}
|
|
401
|
+
function shortenFunctionName(fn) {
|
|
402
|
+
const slashIdx = fn.lastIndexOf("/");
|
|
403
|
+
if (slashIdx >= 0) {
|
|
404
|
+
fn = fn.slice(slashIdx + 1);
|
|
405
|
+
}
|
|
406
|
+
const dotIdx = fn.indexOf(".");
|
|
407
|
+
if (dotIdx >= 0) {
|
|
408
|
+
fn = fn.slice(dotIdx + 1);
|
|
409
|
+
}
|
|
410
|
+
return fn;
|
|
411
|
+
}
|
|
412
|
+
function shortenFilePath(filePath) {
|
|
413
|
+
const parts = filePath.split("/");
|
|
414
|
+
return parts[parts.length - 1];
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/context.ts
|
|
418
|
+
var import_async_hooks = require("async_hooks");
|
|
419
|
+
var import_core3 = require("@tracewayapp/core");
|
|
420
|
+
var asyncLocalStorage = new import_async_hooks.AsyncLocalStorage();
|
|
421
|
+
function getTraceContext() {
|
|
422
|
+
return asyncLocalStorage.getStore();
|
|
423
|
+
}
|
|
424
|
+
function getTraceId() {
|
|
425
|
+
return asyncLocalStorage.getStore()?.traceId;
|
|
426
|
+
}
|
|
427
|
+
function hasTraceContext() {
|
|
428
|
+
return asyncLocalStorage.getStore() !== void 0;
|
|
429
|
+
}
|
|
430
|
+
function withTraceContext(options, fn) {
|
|
431
|
+
const context = {
|
|
432
|
+
traceId: options.traceId ?? (0, import_core3.generateUUID)(),
|
|
433
|
+
isTask: options.isTask ?? false,
|
|
434
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
435
|
+
spans: [],
|
|
436
|
+
attributes: options.attributes ?? {},
|
|
437
|
+
endpoint: options.endpoint,
|
|
438
|
+
clientIP: options.clientIP
|
|
439
|
+
};
|
|
440
|
+
return asyncLocalStorage.run(context, fn);
|
|
441
|
+
}
|
|
442
|
+
function runWithTraceContext(options, fn) {
|
|
443
|
+
return withTraceContext(options, fn);
|
|
444
|
+
}
|
|
445
|
+
function addSpanToContext(span) {
|
|
446
|
+
const ctx = asyncLocalStorage.getStore();
|
|
447
|
+
if (ctx) {
|
|
448
|
+
ctx.spans.push(span);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function setTraceAttribute(key, value) {
|
|
452
|
+
const ctx = asyncLocalStorage.getStore();
|
|
453
|
+
if (ctx) {
|
|
454
|
+
ctx.attributes[key] = value;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function setTraceAttributes(attributes) {
|
|
458
|
+
const ctx = asyncLocalStorage.getStore();
|
|
459
|
+
if (ctx) {
|
|
460
|
+
Object.assign(ctx.attributes, attributes);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
function setTraceResponseInfo(statusCode, bodySize) {
|
|
464
|
+
const ctx = asyncLocalStorage.getStore();
|
|
465
|
+
if (ctx) {
|
|
466
|
+
ctx.statusCode = statusCode;
|
|
467
|
+
ctx.bodySize = bodySize;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
function getTraceSpans() {
|
|
471
|
+
return asyncLocalStorage.getStore()?.spans ?? [];
|
|
472
|
+
}
|
|
473
|
+
function getTraceDuration() {
|
|
474
|
+
const ctx = asyncLocalStorage.getStore();
|
|
475
|
+
if (!ctx) return 0;
|
|
476
|
+
return Date.now() - ctx.startedAt.getTime();
|
|
477
|
+
}
|
|
478
|
+
function forkTraceContext(fn) {
|
|
479
|
+
const parentCtx = asyncLocalStorage.getStore();
|
|
480
|
+
if (!parentCtx) return void 0;
|
|
481
|
+
const forkedContext = {
|
|
482
|
+
...parentCtx,
|
|
483
|
+
spans: [],
|
|
484
|
+
// Forked context gets its own spans
|
|
485
|
+
attributes: { ...parentCtx.attributes }
|
|
486
|
+
};
|
|
487
|
+
return asyncLocalStorage.run(forkedContext, () => {
|
|
488
|
+
const result = fn();
|
|
489
|
+
parentCtx.spans.push(...forkedContext.spans);
|
|
490
|
+
return result;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/traceway.ts
|
|
495
|
+
var os2 = __toESM(require("os"));
|
|
496
|
+
var store = null;
|
|
497
|
+
function getHostname() {
|
|
498
|
+
try {
|
|
499
|
+
const hostname2 = os2.hostname();
|
|
500
|
+
const dotIdx = hostname2.indexOf(".");
|
|
501
|
+
return dotIdx >= 0 ? hostname2.slice(0, dotIdx) : hostname2;
|
|
502
|
+
} catch {
|
|
503
|
+
return "unknown";
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function init(connectionString, options = {}) {
|
|
507
|
+
if (store !== null) {
|
|
508
|
+
throw new Error("Traceway: already initialized. Call shutdown() first.");
|
|
509
|
+
}
|
|
510
|
+
const { token, apiUrl } = (0, import_core4.parseConnectionString)(connectionString);
|
|
511
|
+
store = new CollectionFrameStore({
|
|
512
|
+
apiUrl,
|
|
513
|
+
token,
|
|
514
|
+
debug: options.debug ?? false,
|
|
515
|
+
maxCollectionFrames: options.maxCollectionFrames ?? 12,
|
|
516
|
+
collectionInterval: options.collectionInterval ?? 5e3,
|
|
517
|
+
uploadThrottle: options.uploadThrottle ?? 2e3,
|
|
518
|
+
metricsInterval: options.metricsInterval ?? 3e4,
|
|
519
|
+
version: options.version ?? "",
|
|
520
|
+
serverName: options.serverName ?? getHostname(),
|
|
521
|
+
sampleRate: options.sampleRate ?? 1,
|
|
522
|
+
errorSampleRate: options.errorSampleRate ?? 1
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
function captureException(error) {
|
|
526
|
+
if (!store) return;
|
|
527
|
+
const ctx = getTraceContext();
|
|
528
|
+
captureExceptionWithAttributes(
|
|
529
|
+
error,
|
|
530
|
+
ctx?.attributes,
|
|
531
|
+
ctx?.traceId
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
function captureExceptionWithAttributes(error, attributes, traceId) {
|
|
535
|
+
if (!store) return;
|
|
536
|
+
const ctx = getTraceContext();
|
|
537
|
+
const resolvedTraceId = traceId ?? ctx?.traceId ?? null;
|
|
538
|
+
const resolvedAttrs = attributes ?? ctx?.attributes;
|
|
539
|
+
const isTask = ctx?.isTask ?? false;
|
|
540
|
+
store.addException({
|
|
541
|
+
traceId: resolvedTraceId,
|
|
542
|
+
isTask: isTask || void 0,
|
|
543
|
+
stackTrace: formatErrorStackTrace(error),
|
|
544
|
+
recordedAt: (0, import_core4.nowISO)(),
|
|
545
|
+
attributes: resolvedAttrs,
|
|
546
|
+
isMessage: false
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function captureMessage(msg, attributes) {
|
|
550
|
+
if (!store) return;
|
|
551
|
+
const ctx = getTraceContext();
|
|
552
|
+
const resolvedTraceId = ctx?.traceId ?? null;
|
|
553
|
+
const resolvedAttrs = attributes ?? ctx?.attributes;
|
|
554
|
+
const isTask = ctx?.isTask ?? false;
|
|
555
|
+
store.addException({
|
|
556
|
+
traceId: resolvedTraceId,
|
|
557
|
+
isTask: isTask || void 0,
|
|
558
|
+
stackTrace: msg,
|
|
559
|
+
recordedAt: (0, import_core4.nowISO)(),
|
|
560
|
+
attributes: resolvedAttrs,
|
|
561
|
+
isMessage: true
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
function captureMetric(name, value) {
|
|
565
|
+
if (!store) return;
|
|
566
|
+
store.addMetric({
|
|
567
|
+
name,
|
|
568
|
+
value,
|
|
569
|
+
recordedAt: (0, import_core4.nowISO)()
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
function captureTrace(traceId, endpoint, durationMs, startedAt, statusCode, bodySize, clientIP, attributes, spans) {
|
|
573
|
+
if (!store) return;
|
|
574
|
+
store.addTrace({
|
|
575
|
+
id: traceId,
|
|
576
|
+
endpoint,
|
|
577
|
+
duration: (0, import_core4.msToNanoseconds)(durationMs),
|
|
578
|
+
recordedAt: startedAt.toISOString(),
|
|
579
|
+
statusCode,
|
|
580
|
+
bodySize,
|
|
581
|
+
clientIP,
|
|
582
|
+
attributes,
|
|
583
|
+
spans
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
function captureTask(traceId, taskName, durationMs, startedAt, attributes, spans) {
|
|
587
|
+
if (!store) return;
|
|
588
|
+
store.addTrace({
|
|
589
|
+
id: traceId,
|
|
590
|
+
endpoint: taskName,
|
|
591
|
+
duration: (0, import_core4.msToNanoseconds)(durationMs),
|
|
592
|
+
recordedAt: startedAt.toISOString(),
|
|
593
|
+
statusCode: 0,
|
|
594
|
+
bodySize: 0,
|
|
595
|
+
clientIP: "",
|
|
596
|
+
attributes,
|
|
597
|
+
spans,
|
|
598
|
+
isTask: true
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function startSpan(name) {
|
|
602
|
+
const now = Date.now();
|
|
603
|
+
return {
|
|
604
|
+
id: (0, import_core4.generateUUID)(),
|
|
605
|
+
name,
|
|
606
|
+
startTime: new Date(now).toISOString(),
|
|
607
|
+
startedAt: now
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function endSpan(span, addToContext = true) {
|
|
611
|
+
const durationMs = Date.now() - span.startedAt;
|
|
612
|
+
const completedSpan = {
|
|
613
|
+
id: span.id,
|
|
614
|
+
name: span.name,
|
|
615
|
+
startTime: span.startTime,
|
|
616
|
+
duration: (0, import_core4.msToNanoseconds)(durationMs)
|
|
617
|
+
};
|
|
618
|
+
if (addToContext) {
|
|
619
|
+
addSpanToContext(completedSpan);
|
|
620
|
+
}
|
|
621
|
+
return completedSpan;
|
|
622
|
+
}
|
|
623
|
+
function captureCurrentTrace() {
|
|
624
|
+
if (!store) return;
|
|
625
|
+
const ctx = getTraceContext();
|
|
626
|
+
if (!ctx) return;
|
|
627
|
+
const durationMs = Date.now() - ctx.startedAt.getTime();
|
|
628
|
+
const isError = (ctx.statusCode ?? 0) >= 500;
|
|
629
|
+
if (!shouldSample(isError)) return;
|
|
630
|
+
if (ctx.isTask) {
|
|
631
|
+
store.addTrace({
|
|
632
|
+
id: ctx.traceId,
|
|
633
|
+
endpoint: ctx.endpoint ?? "unknown-task",
|
|
634
|
+
duration: (0, import_core4.msToNanoseconds)(durationMs),
|
|
635
|
+
recordedAt: ctx.startedAt.toISOString(),
|
|
636
|
+
statusCode: 0,
|
|
637
|
+
bodySize: 0,
|
|
638
|
+
clientIP: "",
|
|
639
|
+
attributes: Object.keys(ctx.attributes).length > 0 ? ctx.attributes : void 0,
|
|
640
|
+
spans: ctx.spans.length > 0 ? ctx.spans : void 0,
|
|
641
|
+
isTask: true
|
|
642
|
+
});
|
|
643
|
+
} else {
|
|
644
|
+
store.addTrace({
|
|
645
|
+
id: ctx.traceId,
|
|
646
|
+
endpoint: ctx.endpoint ?? "unknown",
|
|
647
|
+
duration: (0, import_core4.msToNanoseconds)(durationMs),
|
|
648
|
+
recordedAt: ctx.startedAt.toISOString(),
|
|
649
|
+
statusCode: ctx.statusCode ?? 0,
|
|
650
|
+
bodySize: ctx.bodySize ?? 0,
|
|
651
|
+
clientIP: ctx.clientIP ?? "",
|
|
652
|
+
attributes: Object.keys(ctx.attributes).length > 0 ? ctx.attributes : void 0,
|
|
653
|
+
spans: ctx.spans.length > 0 ? ctx.spans : void 0
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
function shouldSample(isError) {
|
|
658
|
+
if (!store) return false;
|
|
659
|
+
const rate = isError ? store.errorSampleRate : store.sampleRate;
|
|
660
|
+
if (rate >= 1) return true;
|
|
661
|
+
if (rate <= 0) return false;
|
|
662
|
+
return Math.random() < rate;
|
|
663
|
+
}
|
|
664
|
+
function measureTask(title, fn) {
|
|
665
|
+
const traceId = (0, import_core4.generateUUID)();
|
|
666
|
+
const start = Date.now();
|
|
667
|
+
const startDate = new Date(start);
|
|
668
|
+
try {
|
|
669
|
+
const result = fn();
|
|
670
|
+
if (result && typeof result.then === "function") {
|
|
671
|
+
result.then(() => {
|
|
672
|
+
const durationMs = Date.now() - start;
|
|
673
|
+
if (shouldSample(false)) {
|
|
674
|
+
captureTask(traceId, title, durationMs, startDate);
|
|
675
|
+
}
|
|
676
|
+
}).catch((err) => {
|
|
677
|
+
const durationMs = Date.now() - start;
|
|
678
|
+
if (shouldSample(true)) {
|
|
679
|
+
captureTask(traceId, title, durationMs, startDate);
|
|
680
|
+
if (err instanceof Error) {
|
|
681
|
+
captureExceptionWithAttributes(err, void 0, traceId);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
throw err;
|
|
685
|
+
});
|
|
686
|
+
} else {
|
|
687
|
+
const durationMs = Date.now() - start;
|
|
688
|
+
if (shouldSample(false)) {
|
|
689
|
+
captureTask(traceId, title, durationMs, startDate);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} catch (err) {
|
|
693
|
+
const durationMs = Date.now() - start;
|
|
694
|
+
if (shouldSample(true)) {
|
|
695
|
+
captureTask(traceId, title, durationMs, startDate);
|
|
696
|
+
if (err instanceof Error) {
|
|
697
|
+
captureExceptionWithAttributes(err, void 0, traceId);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
throw err;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
async function shutdown() {
|
|
704
|
+
if (!store) return;
|
|
705
|
+
const s = store;
|
|
706
|
+
store = null;
|
|
707
|
+
await s.shutdown();
|
|
708
|
+
}
|
|
709
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
710
|
+
0 && (module.exports = {
|
|
711
|
+
CollectionFrameStore,
|
|
712
|
+
addSpanToContext,
|
|
713
|
+
captureCurrentTrace,
|
|
714
|
+
captureException,
|
|
715
|
+
captureExceptionWithAttributes,
|
|
716
|
+
captureMessage,
|
|
717
|
+
captureMetric,
|
|
718
|
+
captureTask,
|
|
719
|
+
captureTrace,
|
|
720
|
+
collectMetrics,
|
|
721
|
+
endSpan,
|
|
722
|
+
forkTraceContext,
|
|
723
|
+
formatErrorStackTrace,
|
|
724
|
+
getTraceContext,
|
|
725
|
+
getTraceDuration,
|
|
726
|
+
getTraceId,
|
|
727
|
+
getTraceSpans,
|
|
728
|
+
hasTraceContext,
|
|
729
|
+
init,
|
|
730
|
+
measureTask,
|
|
731
|
+
resetCpuTracking,
|
|
732
|
+
runWithTraceContext,
|
|
733
|
+
setTraceAttribute,
|
|
734
|
+
setTraceAttributes,
|
|
735
|
+
setTraceResponseInfo,
|
|
736
|
+
shouldSample,
|
|
737
|
+
shutdown,
|
|
738
|
+
startSpan,
|
|
739
|
+
traceContextStorage,
|
|
740
|
+
withTraceContext
|
|
741
|
+
});
|
|
742
|
+
//# sourceMappingURL=index.js.map
|