@deeptracer/core 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 +611 -0
- package/dist/chunk-LSMMIS4A.js +382 -0
- package/dist/index.cjs +408 -0
- package/dist/index.d.cts +221 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.js +8 -0
- package/dist/internal.cjs +405 -0
- package/dist/internal.d.cts +1 -0
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +8 -0
- package/package.json +34 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
// src/batcher.ts
|
|
2
|
+
var Batcher = class {
|
|
3
|
+
constructor(config, onFlush) {
|
|
4
|
+
this.onFlush = onFlush;
|
|
5
|
+
this.batchSize = config.batchSize ?? 50;
|
|
6
|
+
this.flushIntervalMs = config.flushIntervalMs ?? 5e3;
|
|
7
|
+
this.startTimer();
|
|
8
|
+
}
|
|
9
|
+
buffer = [];
|
|
10
|
+
timer = null;
|
|
11
|
+
batchSize;
|
|
12
|
+
flushIntervalMs;
|
|
13
|
+
add(entry) {
|
|
14
|
+
this.buffer.push(entry);
|
|
15
|
+
if (this.buffer.length >= this.batchSize) {
|
|
16
|
+
this.flush();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
flush() {
|
|
20
|
+
if (this.buffer.length === 0) return;
|
|
21
|
+
const entries = [...this.buffer];
|
|
22
|
+
this.buffer = [];
|
|
23
|
+
this.onFlush(entries);
|
|
24
|
+
}
|
|
25
|
+
startTimer() {
|
|
26
|
+
this.timer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
27
|
+
}
|
|
28
|
+
destroy() {
|
|
29
|
+
if (this.timer) clearInterval(this.timer);
|
|
30
|
+
this.flush();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/transport.ts
|
|
35
|
+
var Transport = class {
|
|
36
|
+
constructor(config) {
|
|
37
|
+
this.config = config;
|
|
38
|
+
}
|
|
39
|
+
async sendLogs(logs) {
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(`${this.config.endpoint}/ingest/logs`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: {
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
46
|
+
},
|
|
47
|
+
body: JSON.stringify({
|
|
48
|
+
product: this.config.product,
|
|
49
|
+
service: this.config.service,
|
|
50
|
+
environment: this.config.environment,
|
|
51
|
+
logs
|
|
52
|
+
})
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
console.warn(
|
|
56
|
+
`[@deeptracer/core] Failed to send logs: ${res.status} ${res.statusText}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
console.warn(
|
|
61
|
+
"[@deeptracer/core] Failed to send logs, falling back to console"
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async sendError(error) {
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(`${this.config.endpoint}/ingest/errors`, {
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers: {
|
|
70
|
+
"Content-Type": "application/json",
|
|
71
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify({
|
|
74
|
+
...error,
|
|
75
|
+
product: this.config.product,
|
|
76
|
+
service: this.config.service,
|
|
77
|
+
environment: this.config.environment
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
console.warn(
|
|
82
|
+
`[@deeptracer/core] Failed to send error: ${res.status} ${res.statusText}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
console.warn("[@deeptracer/core] Failed to send error report");
|
|
87
|
+
console.error(error.error_message);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async sendTrace(span) {
|
|
91
|
+
try {
|
|
92
|
+
const res = await fetch(`${this.config.endpoint}/ingest/traces`, {
|
|
93
|
+
method: "POST",
|
|
94
|
+
headers: {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify({
|
|
99
|
+
...span,
|
|
100
|
+
product: this.config.product,
|
|
101
|
+
service: this.config.service,
|
|
102
|
+
environment: this.config.environment
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
if (!res.ok) {
|
|
106
|
+
console.warn(
|
|
107
|
+
`[@deeptracer/core] Failed to send trace: ${res.status} ${res.statusText}`
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
console.warn("[@deeptracer/core] Failed to send trace span");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async sendLLMUsage(report) {
|
|
115
|
+
try {
|
|
116
|
+
const res = await fetch(`${this.config.endpoint}/ingest/llm`, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
"Content-Type": "application/json",
|
|
120
|
+
Authorization: `Bearer ${this.config.apiKey}`
|
|
121
|
+
},
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
...report,
|
|
124
|
+
product: this.config.product,
|
|
125
|
+
service: this.config.service,
|
|
126
|
+
environment: this.config.environment
|
|
127
|
+
})
|
|
128
|
+
});
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
console.warn(
|
|
131
|
+
`[@deeptracer/core] Failed to send LLM usage: ${res.status} ${res.statusText}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
console.warn("[@deeptracer/core] Failed to send LLM usage report");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// src/logger.ts
|
|
141
|
+
function generateId() {
|
|
142
|
+
const bytes = new Uint8Array(8);
|
|
143
|
+
crypto.getRandomValues(bytes);
|
|
144
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
145
|
+
}
|
|
146
|
+
var _originalConsole = {
|
|
147
|
+
log: console.log,
|
|
148
|
+
info: console.info,
|
|
149
|
+
warn: console.warn,
|
|
150
|
+
error: console.error,
|
|
151
|
+
debug: console.debug
|
|
152
|
+
};
|
|
153
|
+
var Logger = class _Logger {
|
|
154
|
+
batcher;
|
|
155
|
+
transport;
|
|
156
|
+
contextName;
|
|
157
|
+
config;
|
|
158
|
+
requestMeta;
|
|
159
|
+
constructor(config, contextName, requestMeta) {
|
|
160
|
+
this.config = config;
|
|
161
|
+
this.contextName = contextName;
|
|
162
|
+
this.requestMeta = requestMeta;
|
|
163
|
+
this.transport = new Transport(config);
|
|
164
|
+
this.batcher = new Batcher(
|
|
165
|
+
{ batchSize: config.batchSize, flushIntervalMs: config.flushIntervalMs },
|
|
166
|
+
(entries) => {
|
|
167
|
+
this.transport.sendLogs(entries);
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
log(level, message, dataOrError, maybeError) {
|
|
172
|
+
let metadata;
|
|
173
|
+
let error;
|
|
174
|
+
if (dataOrError instanceof Error) {
|
|
175
|
+
error = dataOrError;
|
|
176
|
+
} else if (dataOrError && typeof dataOrError === "object" && !Array.isArray(dataOrError)) {
|
|
177
|
+
metadata = dataOrError;
|
|
178
|
+
error = maybeError;
|
|
179
|
+
} else if (dataOrError !== void 0) {
|
|
180
|
+
error = dataOrError;
|
|
181
|
+
}
|
|
182
|
+
if (error instanceof Error) {
|
|
183
|
+
metadata = {
|
|
184
|
+
...metadata,
|
|
185
|
+
error: {
|
|
186
|
+
message: error.message,
|
|
187
|
+
name: error.name,
|
|
188
|
+
stack: error.stack
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const entry = {
|
|
193
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
194
|
+
level,
|
|
195
|
+
message,
|
|
196
|
+
metadata,
|
|
197
|
+
context: this.contextName,
|
|
198
|
+
trace_id: this.requestMeta?.trace_id,
|
|
199
|
+
span_id: this.requestMeta?.span_id,
|
|
200
|
+
request_id: this.requestMeta?.request_id,
|
|
201
|
+
vercel_id: this.requestMeta?.vercel_id
|
|
202
|
+
};
|
|
203
|
+
if (this.config.debug) {
|
|
204
|
+
const prefix = this.contextName ? `[${this.contextName}]` : "";
|
|
205
|
+
const lvl = level.toUpperCase().padEnd(5);
|
|
206
|
+
const consoleFn = level === "error" ? _originalConsole.error : level === "warn" ? _originalConsole.warn : level === "debug" ? _originalConsole.debug : _originalConsole.log;
|
|
207
|
+
if (metadata) {
|
|
208
|
+
consoleFn(`${lvl} ${prefix} ${message}`, metadata);
|
|
209
|
+
} else {
|
|
210
|
+
consoleFn(`${lvl} ${prefix} ${message}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
this.batcher.add(entry);
|
|
214
|
+
}
|
|
215
|
+
/** Log a debug message. */
|
|
216
|
+
debug(message, dataOrError, error) {
|
|
217
|
+
this.log("debug", message, dataOrError, error);
|
|
218
|
+
}
|
|
219
|
+
/** Log an informational message. */
|
|
220
|
+
info(message, dataOrError, error) {
|
|
221
|
+
this.log("info", message, dataOrError, error);
|
|
222
|
+
}
|
|
223
|
+
/** Log a warning. */
|
|
224
|
+
warn(message, dataOrError, error) {
|
|
225
|
+
this.log("warn", message, dataOrError, error);
|
|
226
|
+
}
|
|
227
|
+
/** Log an error. */
|
|
228
|
+
error(message, dataOrError, error) {
|
|
229
|
+
this.log("error", message, dataOrError, error);
|
|
230
|
+
}
|
|
231
|
+
/** Create a context-scoped logger. All logs include the context name. */
|
|
232
|
+
withContext(name) {
|
|
233
|
+
return new _Logger(this.config, name, this.requestMeta);
|
|
234
|
+
}
|
|
235
|
+
/** Create a request-scoped logger that extracts trace context from headers. */
|
|
236
|
+
forRequest(request) {
|
|
237
|
+
const vercelId = request.headers.get("x-vercel-id") || void 0;
|
|
238
|
+
const requestId = request.headers.get("x-request-id") || void 0;
|
|
239
|
+
const traceId = request.headers.get("x-trace-id") || void 0;
|
|
240
|
+
const spanId = request.headers.get("x-span-id") || void 0;
|
|
241
|
+
return new _Logger(this.config, this.contextName, {
|
|
242
|
+
trace_id: traceId,
|
|
243
|
+
span_id: spanId,
|
|
244
|
+
request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
|
|
245
|
+
vercel_id: vercelId
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/** Capture and report an error immediately (not batched). */
|
|
249
|
+
captureError(error, context) {
|
|
250
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
251
|
+
const report = {
|
|
252
|
+
error_message: err.message,
|
|
253
|
+
stack_trace: err.stack || "",
|
|
254
|
+
severity: context?.severity || "medium",
|
|
255
|
+
context: context?.context,
|
|
256
|
+
trace_id: this.requestMeta?.trace_id,
|
|
257
|
+
user_id: context?.userId,
|
|
258
|
+
breadcrumbs: context?.breadcrumbs
|
|
259
|
+
};
|
|
260
|
+
this.transport.sendError(report);
|
|
261
|
+
}
|
|
262
|
+
/** Track LLM usage. Sends to /ingest/llm and logs for visibility. */
|
|
263
|
+
llmUsage(report) {
|
|
264
|
+
this.transport.sendLLMUsage({
|
|
265
|
+
model: report.model,
|
|
266
|
+
provider: report.provider,
|
|
267
|
+
operation: report.operation,
|
|
268
|
+
input_tokens: report.inputTokens,
|
|
269
|
+
output_tokens: report.outputTokens,
|
|
270
|
+
cost_usd: report.costUsd || 0,
|
|
271
|
+
latency_ms: report.latencyMs,
|
|
272
|
+
metadata: report.metadata
|
|
273
|
+
});
|
|
274
|
+
this.log("info", `LLM call: ${report.model} (${report.operation})`, {
|
|
275
|
+
llm_usage: {
|
|
276
|
+
model: report.model,
|
|
277
|
+
provider: report.provider,
|
|
278
|
+
operation: report.operation,
|
|
279
|
+
input_tokens: report.inputTokens,
|
|
280
|
+
output_tokens: report.outputTokens,
|
|
281
|
+
latency_ms: report.latencyMs
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/** Start a span with automatic lifecycle (callback-based, recommended). */
|
|
286
|
+
startSpan(operation, fn) {
|
|
287
|
+
const inactive = this.startInactiveSpan(operation);
|
|
288
|
+
const span = {
|
|
289
|
+
traceId: inactive.traceId,
|
|
290
|
+
spanId: inactive.spanId,
|
|
291
|
+
parentSpanId: inactive.parentSpanId,
|
|
292
|
+
operation: inactive.operation,
|
|
293
|
+
getHeaders: () => inactive.getHeaders()
|
|
294
|
+
};
|
|
295
|
+
let result;
|
|
296
|
+
try {
|
|
297
|
+
result = fn(span);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
inactive.end({ status: "error" });
|
|
300
|
+
throw err;
|
|
301
|
+
}
|
|
302
|
+
if (result instanceof Promise) {
|
|
303
|
+
return result.then(
|
|
304
|
+
(value) => {
|
|
305
|
+
inactive.end({ status: "ok" });
|
|
306
|
+
return value;
|
|
307
|
+
},
|
|
308
|
+
(err) => {
|
|
309
|
+
inactive.end({ status: "error" });
|
|
310
|
+
throw err;
|
|
311
|
+
}
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
inactive.end({ status: "ok" });
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
/** Start a span with manual lifecycle. You must call span.end(). */
|
|
318
|
+
startInactiveSpan(operation) {
|
|
319
|
+
const traceId = this.requestMeta?.trace_id || generateId();
|
|
320
|
+
const parentSpanId = this.requestMeta?.span_id || "";
|
|
321
|
+
const spanId = generateId();
|
|
322
|
+
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
323
|
+
const startMs = Date.now();
|
|
324
|
+
const childMeta = { ...this.requestMeta, trace_id: traceId, span_id: spanId };
|
|
325
|
+
const span = {
|
|
326
|
+
traceId,
|
|
327
|
+
spanId,
|
|
328
|
+
parentSpanId,
|
|
329
|
+
operation,
|
|
330
|
+
end: (options) => {
|
|
331
|
+
const durationMs = Date.now() - startMs;
|
|
332
|
+
const spanData = {
|
|
333
|
+
trace_id: traceId,
|
|
334
|
+
span_id: spanId,
|
|
335
|
+
parent_span_id: parentSpanId,
|
|
336
|
+
operation,
|
|
337
|
+
start_time: startTime,
|
|
338
|
+
duration_ms: durationMs,
|
|
339
|
+
status: options?.status || "ok",
|
|
340
|
+
metadata: options?.metadata
|
|
341
|
+
};
|
|
342
|
+
this.transport.sendTrace(spanData);
|
|
343
|
+
},
|
|
344
|
+
startSpan: (childOp, fn) => {
|
|
345
|
+
const childLogger = new _Logger(this.config, this.contextName, childMeta);
|
|
346
|
+
return childLogger.startSpan(childOp, fn);
|
|
347
|
+
},
|
|
348
|
+
startInactiveSpan: (childOp) => {
|
|
349
|
+
const childLogger = new _Logger(this.config, this.contextName, childMeta);
|
|
350
|
+
return childLogger.startInactiveSpan(childOp);
|
|
351
|
+
},
|
|
352
|
+
getHeaders: () => ({
|
|
353
|
+
"x-trace-id": traceId,
|
|
354
|
+
"x-span-id": spanId
|
|
355
|
+
})
|
|
356
|
+
};
|
|
357
|
+
return span;
|
|
358
|
+
}
|
|
359
|
+
/** Wrap a function with automatic tracing and error capture. */
|
|
360
|
+
wrap(operation, fn) {
|
|
361
|
+
return (...args) => {
|
|
362
|
+
return this.startSpan(operation, () => fn(...args));
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/** Immediately flush all batched log entries. */
|
|
366
|
+
flush() {
|
|
367
|
+
this.batcher.flush();
|
|
368
|
+
}
|
|
369
|
+
/** Stop the batch timer and flush remaining logs. */
|
|
370
|
+
destroy() {
|
|
371
|
+
this.batcher.destroy();
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
function createLogger(config) {
|
|
375
|
+
return new Logger(config);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export {
|
|
379
|
+
_originalConsole,
|
|
380
|
+
Logger,
|
|
381
|
+
createLogger
|
|
382
|
+
};
|