@deeptracer/core 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-XVDM75HZ.js +601 -0
- package/dist/index.cjs +333 -112
- package/dist/index.d.cts +5 -219
- package/dist/index.d.ts +5 -219
- package/dist/index.js +5 -1
- package/dist/internal-DdKQRgCs.d.cts +375 -0
- package/dist/internal-DdKQRgCs.d.ts +375 -0
- package/dist/internal.cjs +329 -112
- package/dist/internal.d.cts +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-LSMMIS4A.js +0 -382
package/dist/internal.cjs
CHANGED
|
@@ -52,118 +52,167 @@ var Batcher = class {
|
|
|
52
52
|
startTimer() {
|
|
53
53
|
this.timer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
54
54
|
}
|
|
55
|
-
destroy() {
|
|
55
|
+
async destroy() {
|
|
56
56
|
if (this.timer) clearInterval(this.timer);
|
|
57
|
+
this.timer = null;
|
|
57
58
|
this.flush();
|
|
58
59
|
}
|
|
59
60
|
};
|
|
60
61
|
|
|
62
|
+
// src/version.ts
|
|
63
|
+
var SDK_VERSION = "0.3.1";
|
|
64
|
+
var SDK_NAME = "core";
|
|
65
|
+
|
|
61
66
|
// src/transport.ts
|
|
62
67
|
var Transport = class {
|
|
63
68
|
constructor(config) {
|
|
64
69
|
this.config = config;
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
inFlightRequests = /* @__PURE__ */ new Set();
|
|
72
|
+
/**
|
|
73
|
+
* Send a request with automatic retry and exponential backoff.
|
|
74
|
+
* Retries up to `maxRetries` times on network errors and 5xx responses.
|
|
75
|
+
* Does NOT retry on 4xx (client errors — bad payload, auth failure, etc.).
|
|
76
|
+
*/
|
|
77
|
+
async sendWithRetry(url, body, label, maxRetries = 3) {
|
|
78
|
+
const baseDelays = [1e3, 2e3, 4e3];
|
|
79
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
80
|
+
try {
|
|
81
|
+
const res = await fetch(url, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json",
|
|
85
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
86
|
+
"x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify(body)
|
|
89
|
+
});
|
|
90
|
+
if (res.ok) return;
|
|
91
|
+
if (res.status >= 400 && res.status < 500) {
|
|
92
|
+
console.warn(
|
|
93
|
+
`[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText}`
|
|
94
|
+
);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (attempt < maxRetries) {
|
|
98
|
+
await this.sleep(this.jitter(baseDelays[attempt]));
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
82
101
|
console.warn(
|
|
83
|
-
`[@deeptracer/core] Failed to send
|
|
102
|
+
`[@deeptracer/core] Failed to send ${label}: ${res.status} ${res.statusText} (exhausted ${maxRetries} retries)`
|
|
84
103
|
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
async sendError(error) {
|
|
93
|
-
try {
|
|
94
|
-
const res = await fetch(`${this.config.endpoint}/ingest/errors`, {
|
|
95
|
-
method: "POST",
|
|
96
|
-
headers: {
|
|
97
|
-
"Content-Type": "application/json",
|
|
98
|
-
Authorization: `Bearer ${this.config.apiKey}`
|
|
99
|
-
},
|
|
100
|
-
body: JSON.stringify({
|
|
101
|
-
...error,
|
|
102
|
-
product: this.config.product,
|
|
103
|
-
service: this.config.service,
|
|
104
|
-
environment: this.config.environment
|
|
105
|
-
})
|
|
106
|
-
});
|
|
107
|
-
if (!res.ok) {
|
|
104
|
+
} catch {
|
|
105
|
+
if (attempt < maxRetries) {
|
|
106
|
+
await this.sleep(this.jitter(baseDelays[attempt]));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
108
109
|
console.warn(
|
|
109
|
-
`[@deeptracer/core] Failed to send
|
|
110
|
+
`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`
|
|
110
111
|
);
|
|
111
112
|
}
|
|
112
|
-
} catch {
|
|
113
|
-
console.warn("[@deeptracer/core] Failed to send error report");
|
|
114
|
-
console.error(error.error_message);
|
|
115
113
|
}
|
|
116
114
|
}
|
|
115
|
+
/** Add +/- 20% jitter to a delay to prevent thundering herd. */
|
|
116
|
+
jitter(ms) {
|
|
117
|
+
const factor = 0.8 + Math.random() * 0.4;
|
|
118
|
+
return Math.round(ms * factor);
|
|
119
|
+
}
|
|
120
|
+
sleep(ms) {
|
|
121
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
122
|
+
}
|
|
123
|
+
/** Track an in-flight request and remove it when done. */
|
|
124
|
+
track(promise) {
|
|
125
|
+
this.inFlightRequests.add(promise);
|
|
126
|
+
promise.finally(() => this.inFlightRequests.delete(promise));
|
|
127
|
+
}
|
|
128
|
+
async sendLogs(logs) {
|
|
129
|
+
const p = this.sendWithRetry(
|
|
130
|
+
`${this.config.endpoint}/ingest/logs`,
|
|
131
|
+
{
|
|
132
|
+
product: this.config.product,
|
|
133
|
+
service: this.config.service,
|
|
134
|
+
environment: this.config.environment,
|
|
135
|
+
logs
|
|
136
|
+
},
|
|
137
|
+
"logs"
|
|
138
|
+
);
|
|
139
|
+
this.track(p);
|
|
140
|
+
return p;
|
|
141
|
+
}
|
|
142
|
+
async sendError(error) {
|
|
143
|
+
const p = this.sendWithRetry(
|
|
144
|
+
`${this.config.endpoint}/ingest/errors`,
|
|
145
|
+
{
|
|
146
|
+
...error,
|
|
147
|
+
product: this.config.product,
|
|
148
|
+
service: this.config.service,
|
|
149
|
+
environment: this.config.environment
|
|
150
|
+
},
|
|
151
|
+
"error"
|
|
152
|
+
);
|
|
153
|
+
this.track(p);
|
|
154
|
+
return p;
|
|
155
|
+
}
|
|
117
156
|
async sendTrace(span) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
})
|
|
131
|
-
});
|
|
132
|
-
if (!res.ok) {
|
|
133
|
-
console.warn(
|
|
134
|
-
`[@deeptracer/core] Failed to send trace: ${res.status} ${res.statusText}`
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
} catch {
|
|
138
|
-
console.warn("[@deeptracer/core] Failed to send trace span");
|
|
139
|
-
}
|
|
157
|
+
const p = this.sendWithRetry(
|
|
158
|
+
`${this.config.endpoint}/ingest/traces`,
|
|
159
|
+
{
|
|
160
|
+
...span,
|
|
161
|
+
product: this.config.product,
|
|
162
|
+
service: this.config.service,
|
|
163
|
+
environment: this.config.environment
|
|
164
|
+
},
|
|
165
|
+
"trace"
|
|
166
|
+
);
|
|
167
|
+
this.track(p);
|
|
168
|
+
return p;
|
|
140
169
|
}
|
|
141
170
|
async sendLLMUsage(report) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
171
|
+
const p = this.sendWithRetry(
|
|
172
|
+
`${this.config.endpoint}/ingest/llm`,
|
|
173
|
+
{
|
|
174
|
+
...report,
|
|
175
|
+
product: this.config.product,
|
|
176
|
+
service: this.config.service,
|
|
177
|
+
environment: this.config.environment
|
|
178
|
+
},
|
|
179
|
+
"LLM usage"
|
|
180
|
+
);
|
|
181
|
+
this.track(p);
|
|
182
|
+
return p;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Wait for all in-flight requests to complete, with a timeout.
|
|
186
|
+
* Used by `logger.destroy()` to ensure data is sent before process exit.
|
|
187
|
+
*
|
|
188
|
+
* @param timeoutMs - Maximum time to wait (default: 2000ms)
|
|
189
|
+
*/
|
|
190
|
+
async drain(timeoutMs = 2e3) {
|
|
191
|
+
if (this.inFlightRequests.size === 0) return;
|
|
192
|
+
const allDone = Promise.all(this.inFlightRequests).then(() => {
|
|
193
|
+
});
|
|
194
|
+
const timeout = new Promise((resolve) => setTimeout(resolve, timeoutMs));
|
|
195
|
+
await Promise.race([allDone, timeout]);
|
|
164
196
|
}
|
|
165
197
|
};
|
|
166
198
|
|
|
199
|
+
// src/state.ts
|
|
200
|
+
function createLoggerState(maxBreadcrumbs) {
|
|
201
|
+
return {
|
|
202
|
+
user: null,
|
|
203
|
+
tags: {},
|
|
204
|
+
contexts: {},
|
|
205
|
+
breadcrumbs: [],
|
|
206
|
+
maxBreadcrumbs
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function addBreadcrumb(state, breadcrumb) {
|
|
210
|
+
state.breadcrumbs.push(breadcrumb);
|
|
211
|
+
if (state.breadcrumbs.length > state.maxBreadcrumbs) {
|
|
212
|
+
state.breadcrumbs.shift();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
167
216
|
// src/logger.ts
|
|
168
217
|
function generateId() {
|
|
169
218
|
const bytes = new Uint8Array(8);
|
|
@@ -182,11 +231,13 @@ var Logger = class _Logger {
|
|
|
182
231
|
transport;
|
|
183
232
|
contextName;
|
|
184
233
|
config;
|
|
234
|
+
state;
|
|
185
235
|
requestMeta;
|
|
186
|
-
constructor(config, contextName, requestMeta) {
|
|
236
|
+
constructor(config, contextName, requestMeta, state) {
|
|
187
237
|
this.config = config;
|
|
188
238
|
this.contextName = contextName;
|
|
189
239
|
this.requestMeta = requestMeta;
|
|
240
|
+
this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
|
|
190
241
|
this.transport = new Transport(config);
|
|
191
242
|
this.batcher = new Batcher(
|
|
192
243
|
{ batchSize: config.batchSize, flushIntervalMs: config.flushIntervalMs },
|
|
@@ -195,6 +246,111 @@ var Logger = class _Logger {
|
|
|
195
246
|
}
|
|
196
247
|
);
|
|
197
248
|
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// User context
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* Set the current user context. Attached to all subsequent logs, errors, spans, and LLM reports.
|
|
254
|
+
* Shared across all child loggers (withContext, forRequest).
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* logger.setUser({ id: "u_123", email: "user@example.com", plan: "pro" })
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
setUser(user) {
|
|
262
|
+
this.state.user = user;
|
|
263
|
+
}
|
|
264
|
+
/** Clear the current user context. */
|
|
265
|
+
clearUser() {
|
|
266
|
+
this.state.user = null;
|
|
267
|
+
}
|
|
268
|
+
// ---------------------------------------------------------------------------
|
|
269
|
+
// Tags & Context
|
|
270
|
+
// ---------------------------------------------------------------------------
|
|
271
|
+
/**
|
|
272
|
+
* Set global tags (flat string key-values). Merged into all events' metadata as `_tags`.
|
|
273
|
+
* Tags are indexed and searchable on the dashboard.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```ts
|
|
277
|
+
* logger.setTags({ release: "1.2.3", region: "us-east-1" })
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
setTags(tags) {
|
|
281
|
+
Object.assign(this.state.tags, tags);
|
|
282
|
+
}
|
|
283
|
+
/** Clear all global tags. */
|
|
284
|
+
clearTags() {
|
|
285
|
+
this.state.tags = {};
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Set a named context block. Merged into metadata as `_contexts.{name}`.
|
|
289
|
+
* Contexts are structured objects attached for reference (not necessarily indexed).
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```ts
|
|
293
|
+
* logger.setContext("server", { hostname: "web-3", memory: "4gb" })
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
setContext(name, data) {
|
|
297
|
+
this.state.contexts[name] = data;
|
|
298
|
+
}
|
|
299
|
+
/** Clear a specific context block, or all contexts if no name is given. */
|
|
300
|
+
clearContext(name) {
|
|
301
|
+
if (name) {
|
|
302
|
+
delete this.state.contexts[name];
|
|
303
|
+
} else {
|
|
304
|
+
this.state.contexts = {};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Breadcrumbs
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
/**
|
|
311
|
+
* Manually add a breadcrumb to the trail.
|
|
312
|
+
* Breadcrumbs are also recorded automatically for every log, span, and error.
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```ts
|
|
316
|
+
* logger.addBreadcrumb({ type: "http", message: "POST /api/checkout" })
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
addBreadcrumb(breadcrumb) {
|
|
320
|
+
addBreadcrumb(this.state, {
|
|
321
|
+
type: breadcrumb.type,
|
|
322
|
+
message: breadcrumb.message,
|
|
323
|
+
timestamp: breadcrumb.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// Internal helpers
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
/** Merge user, tags, and contexts from shared state into event metadata. */
|
|
330
|
+
mergeStateMetadata(metadata) {
|
|
331
|
+
const { user, tags, contexts } = this.state;
|
|
332
|
+
const hasUser = user !== null;
|
|
333
|
+
const hasTags = Object.keys(tags).length > 0;
|
|
334
|
+
const hasContexts = Object.keys(contexts).length > 0;
|
|
335
|
+
if (!hasUser && !hasTags && !hasContexts && !metadata) return void 0;
|
|
336
|
+
const result = { ...metadata };
|
|
337
|
+
if (hasUser) result.user = user;
|
|
338
|
+
if (hasTags) result._tags = { ...tags };
|
|
339
|
+
if (hasContexts) result._contexts = { ...contexts };
|
|
340
|
+
return result;
|
|
341
|
+
}
|
|
342
|
+
/** Run the beforeSend hook. If the hook throws, pass the event through. */
|
|
343
|
+
applyBeforeSend(event) {
|
|
344
|
+
if (!this.config.beforeSend) return event;
|
|
345
|
+
try {
|
|
346
|
+
return this.config.beforeSend(event);
|
|
347
|
+
} catch {
|
|
348
|
+
return event;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
// Logging
|
|
353
|
+
// ---------------------------------------------------------------------------
|
|
198
354
|
log(level, message, dataOrError, maybeError) {
|
|
199
355
|
let metadata;
|
|
200
356
|
let error;
|
|
@@ -216,6 +372,7 @@ var Logger = class _Logger {
|
|
|
216
372
|
}
|
|
217
373
|
};
|
|
218
374
|
}
|
|
375
|
+
metadata = this.mergeStateMetadata(metadata);
|
|
219
376
|
const entry = {
|
|
220
377
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
221
378
|
level,
|
|
@@ -227,17 +384,25 @@ var Logger = class _Logger {
|
|
|
227
384
|
request_id: this.requestMeta?.request_id,
|
|
228
385
|
vercel_id: this.requestMeta?.vercel_id
|
|
229
386
|
};
|
|
387
|
+
const hookResult = this.applyBeforeSend({ type: "log", data: entry });
|
|
388
|
+
if (hookResult === null) return;
|
|
389
|
+
const finalEntry = hookResult.data;
|
|
230
390
|
if (this.config.debug) {
|
|
231
391
|
const prefix = this.contextName ? `[${this.contextName}]` : "";
|
|
232
392
|
const lvl = level.toUpperCase().padEnd(5);
|
|
233
393
|
const consoleFn = level === "error" ? _originalConsole.error : level === "warn" ? _originalConsole.warn : level === "debug" ? _originalConsole.debug : _originalConsole.log;
|
|
234
|
-
if (metadata) {
|
|
235
|
-
consoleFn(`${lvl} ${prefix} ${message}`, metadata);
|
|
394
|
+
if (finalEntry.metadata) {
|
|
395
|
+
consoleFn(`${lvl} ${prefix} ${message}`, finalEntry.metadata);
|
|
236
396
|
} else {
|
|
237
397
|
consoleFn(`${lvl} ${prefix} ${message}`);
|
|
238
398
|
}
|
|
239
399
|
}
|
|
240
|
-
this.
|
|
400
|
+
addBreadcrumb(this.state, {
|
|
401
|
+
type: "log",
|
|
402
|
+
message: `[${level}] ${message}`,
|
|
403
|
+
timestamp: entry.timestamp
|
|
404
|
+
});
|
|
405
|
+
this.batcher.add(finalEntry);
|
|
241
406
|
}
|
|
242
407
|
/** Log a debug message. */
|
|
243
408
|
debug(message, dataOrError, error) {
|
|
@@ -255,11 +420,14 @@ var Logger = class _Logger {
|
|
|
255
420
|
error(message, dataOrError, error) {
|
|
256
421
|
this.log("error", message, dataOrError, error);
|
|
257
422
|
}
|
|
258
|
-
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
// Child loggers
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
/** Create a context-scoped logger. All logs include the context name. Shares state with parent. */
|
|
259
427
|
withContext(name) {
|
|
260
|
-
return new _Logger(this.config, name, this.requestMeta);
|
|
428
|
+
return new _Logger(this.config, name, this.requestMeta, this.state);
|
|
261
429
|
}
|
|
262
|
-
/** Create a request-scoped logger that extracts trace context from headers. */
|
|
430
|
+
/** Create a request-scoped logger that extracts trace context from headers. Shares state with parent. */
|
|
263
431
|
forRequest(request) {
|
|
264
432
|
const vercelId = request.headers.get("x-vercel-id") || void 0;
|
|
265
433
|
const requestId = request.headers.get("x-request-id") || void 0;
|
|
@@ -270,25 +438,46 @@ var Logger = class _Logger {
|
|
|
270
438
|
span_id: spanId,
|
|
271
439
|
request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
|
|
272
440
|
vercel_id: vercelId
|
|
273
|
-
});
|
|
441
|
+
}, this.state);
|
|
274
442
|
}
|
|
275
|
-
|
|
443
|
+
// ---------------------------------------------------------------------------
|
|
444
|
+
// Error capture
|
|
445
|
+
// ---------------------------------------------------------------------------
|
|
446
|
+
/**
|
|
447
|
+
* Capture and report an error immediately (not batched).
|
|
448
|
+
* Automatically attaches breadcrumbs from the buffer and user context.
|
|
449
|
+
*/
|
|
276
450
|
captureError(error, context) {
|
|
277
451
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
452
|
+
addBreadcrumb(this.state, {
|
|
453
|
+
type: "error",
|
|
454
|
+
message: err.message,
|
|
455
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
456
|
+
});
|
|
457
|
+
const enrichedContext = { ...context?.context };
|
|
458
|
+
if (this.state.user) enrichedContext.user = this.state.user;
|
|
459
|
+
if (Object.keys(this.state.tags).length > 0) enrichedContext._tags = { ...this.state.tags };
|
|
460
|
+
if (Object.keys(this.state.contexts).length > 0) enrichedContext._contexts = { ...this.state.contexts };
|
|
278
461
|
const report = {
|
|
279
462
|
error_message: err.message,
|
|
280
463
|
stack_trace: err.stack || "",
|
|
281
464
|
severity: context?.severity || "medium",
|
|
282
|
-
context:
|
|
465
|
+
context: Object.keys(enrichedContext).length > 0 ? enrichedContext : void 0,
|
|
283
466
|
trace_id: this.requestMeta?.trace_id,
|
|
284
|
-
user_id: context?.userId,
|
|
285
|
-
breadcrumbs: context?.breadcrumbs
|
|
467
|
+
user_id: context?.userId || this.state.user?.id,
|
|
468
|
+
breadcrumbs: context?.breadcrumbs || [...this.state.breadcrumbs]
|
|
286
469
|
};
|
|
287
|
-
this.
|
|
470
|
+
const hookResult = this.applyBeforeSend({ type: "error", data: report });
|
|
471
|
+
if (hookResult === null) return;
|
|
472
|
+
this.transport.sendError(hookResult.data);
|
|
288
473
|
}
|
|
474
|
+
// ---------------------------------------------------------------------------
|
|
475
|
+
// LLM usage
|
|
476
|
+
// ---------------------------------------------------------------------------
|
|
289
477
|
/** Track LLM usage. Sends to /ingest/llm and logs for visibility. */
|
|
290
478
|
llmUsage(report) {
|
|
291
|
-
this.
|
|
479
|
+
const metadata = this.mergeStateMetadata(report.metadata);
|
|
480
|
+
const payload = {
|
|
292
481
|
model: report.model,
|
|
293
482
|
provider: report.provider,
|
|
294
483
|
operation: report.operation,
|
|
@@ -296,8 +485,11 @@ var Logger = class _Logger {
|
|
|
296
485
|
output_tokens: report.outputTokens,
|
|
297
486
|
cost_usd: report.costUsd || 0,
|
|
298
487
|
latency_ms: report.latencyMs,
|
|
299
|
-
metadata
|
|
300
|
-
}
|
|
488
|
+
metadata
|
|
489
|
+
};
|
|
490
|
+
const hookResult = this.applyBeforeSend({ type: "llm", data: report });
|
|
491
|
+
if (hookResult === null) return;
|
|
492
|
+
this.transport.sendLLMUsage(payload);
|
|
301
493
|
this.log("info", `LLM call: ${report.model} (${report.operation})`, {
|
|
302
494
|
llm_usage: {
|
|
303
495
|
model: report.model,
|
|
@@ -309,6 +501,9 @@ var Logger = class _Logger {
|
|
|
309
501
|
}
|
|
310
502
|
});
|
|
311
503
|
}
|
|
504
|
+
// ---------------------------------------------------------------------------
|
|
505
|
+
// Tracing
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
312
507
|
/** Start a span with automatic lifecycle (callback-based, recommended). */
|
|
313
508
|
startSpan(operation, fn) {
|
|
314
509
|
const inactive = this.startInactiveSpan(operation);
|
|
@@ -349,6 +544,11 @@ var Logger = class _Logger {
|
|
|
349
544
|
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
350
545
|
const startMs = Date.now();
|
|
351
546
|
const childMeta = { ...this.requestMeta, trace_id: traceId, span_id: spanId };
|
|
547
|
+
addBreadcrumb(this.state, {
|
|
548
|
+
type: "function",
|
|
549
|
+
message: operation,
|
|
550
|
+
timestamp: startTime
|
|
551
|
+
});
|
|
352
552
|
const span = {
|
|
353
553
|
traceId,
|
|
354
554
|
spanId,
|
|
@@ -364,16 +564,18 @@ var Logger = class _Logger {
|
|
|
364
564
|
start_time: startTime,
|
|
365
565
|
duration_ms: durationMs,
|
|
366
566
|
status: options?.status || "ok",
|
|
367
|
-
metadata: options?.metadata
|
|
567
|
+
metadata: this.mergeStateMetadata(options?.metadata)
|
|
368
568
|
};
|
|
369
|
-
this.
|
|
569
|
+
const hookResult = this.applyBeforeSend({ type: "trace", data: spanData });
|
|
570
|
+
if (hookResult === null) return;
|
|
571
|
+
this.transport.sendTrace(hookResult.data);
|
|
370
572
|
},
|
|
371
573
|
startSpan: (childOp, fn) => {
|
|
372
|
-
const childLogger = new _Logger(this.config, this.contextName, childMeta);
|
|
574
|
+
const childLogger = new _Logger(this.config, this.contextName, childMeta, this.state);
|
|
373
575
|
return childLogger.startSpan(childOp, fn);
|
|
374
576
|
},
|
|
375
577
|
startInactiveSpan: (childOp) => {
|
|
376
|
-
const childLogger = new _Logger(this.config, this.contextName, childMeta);
|
|
578
|
+
const childLogger = new _Logger(this.config, this.contextName, childMeta, this.state);
|
|
377
579
|
return childLogger.startInactiveSpan(childOp);
|
|
378
580
|
},
|
|
379
581
|
getHeaders: () => ({
|
|
@@ -389,13 +591,28 @@ var Logger = class _Logger {
|
|
|
389
591
|
return this.startSpan(operation, () => fn(...args));
|
|
390
592
|
};
|
|
391
593
|
}
|
|
594
|
+
// ---------------------------------------------------------------------------
|
|
595
|
+
// Lifecycle
|
|
596
|
+
// ---------------------------------------------------------------------------
|
|
392
597
|
/** Immediately flush all batched log entries. */
|
|
393
598
|
flush() {
|
|
394
599
|
this.batcher.flush();
|
|
395
600
|
}
|
|
396
|
-
/**
|
|
397
|
-
|
|
398
|
-
|
|
601
|
+
/**
|
|
602
|
+
* Stop the batch timer, flush remaining logs, and wait for in-flight requests.
|
|
603
|
+
*
|
|
604
|
+
* @param timeoutMs - Max time to wait for in-flight requests (default: 2000ms)
|
|
605
|
+
* @returns Promise that resolves when all data is sent or timeout is reached
|
|
606
|
+
*
|
|
607
|
+
* @example
|
|
608
|
+
* ```ts
|
|
609
|
+
* await logger.destroy()
|
|
610
|
+
* process.exit(0) // safe — data is confirmed sent
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
async destroy(timeoutMs) {
|
|
614
|
+
await this.batcher.destroy();
|
|
615
|
+
await this.transport.drain(timeoutMs);
|
|
399
616
|
}
|
|
400
617
|
};
|
|
401
618
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/internal.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Logger, LoggerConfig, MiddlewareOptions, _ as _originalConsole } from './
|
|
1
|
+
export { d as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, _ as _originalConsole } from './internal-DdKQRgCs.cjs';
|
package/dist/internal.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Logger, LoggerConfig, MiddlewareOptions, _ as _originalConsole } from './
|
|
1
|
+
export { d as Logger, e as LoggerConfig, h as LoggerState, M as MiddlewareOptions, _ as _originalConsole } from './internal-DdKQRgCs.js';
|
package/dist/internal.js
CHANGED