@deeptracer/core 0.3.0 → 0.4.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-BRUVRV5O.js → chunk-YBQXVNNR.js} +154 -80
- package/dist/index.cjs +118 -46
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/internal.cjs +186 -48
- package/dist/internal.d.cts +66 -1
- package/dist/internal.d.ts +66 -1
- package/dist/internal.js +68 -3
- package/dist/{internal-DdKQRgCs.d.cts → logger-BDTEt7Gi.d.cts} +60 -23
- package/dist/{internal-DdKQRgCs.d.ts → logger-BDTEt7Gi.d.ts} +60 -23
- package/package.json +11 -3
|
@@ -1,47 +1,22 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var SDK_VERSION = "0.
|
|
2
|
+
var SDK_VERSION = "0.4.1";
|
|
3
3
|
var SDK_NAME = "core";
|
|
4
4
|
|
|
5
|
-
// src/batcher.ts
|
|
6
|
-
var Batcher = class {
|
|
7
|
-
constructor(config, onFlush) {
|
|
8
|
-
this.onFlush = onFlush;
|
|
9
|
-
this.batchSize = config.batchSize ?? 50;
|
|
10
|
-
this.flushIntervalMs = config.flushIntervalMs ?? 5e3;
|
|
11
|
-
this.startTimer();
|
|
12
|
-
}
|
|
13
|
-
buffer = [];
|
|
14
|
-
timer = null;
|
|
15
|
-
batchSize;
|
|
16
|
-
flushIntervalMs;
|
|
17
|
-
add(entry) {
|
|
18
|
-
this.buffer.push(entry);
|
|
19
|
-
if (this.buffer.length >= this.batchSize) {
|
|
20
|
-
this.flush();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
flush() {
|
|
24
|
-
if (this.buffer.length === 0) return;
|
|
25
|
-
const entries = [...this.buffer];
|
|
26
|
-
this.buffer = [];
|
|
27
|
-
this.onFlush(entries);
|
|
28
|
-
}
|
|
29
|
-
startTimer() {
|
|
30
|
-
this.timer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
31
|
-
}
|
|
32
|
-
async destroy() {
|
|
33
|
-
if (this.timer) clearInterval(this.timer);
|
|
34
|
-
this.timer = null;
|
|
35
|
-
this.flush();
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
|
|
39
5
|
// src/transport.ts
|
|
40
6
|
var Transport = class {
|
|
41
7
|
constructor(config) {
|
|
42
8
|
this.config = config;
|
|
9
|
+
if (config.secretKey?.startsWith("dt_secret_") && typeof globalThis.window !== "undefined") {
|
|
10
|
+
console.error(
|
|
11
|
+
"[@deeptracer/core] WARNING: `secretKey` (dt_secret_...) detected in a browser bundle. This exposes your server key to end users. Use `publicKey` (dt_public_...) for client-side code."
|
|
12
|
+
);
|
|
13
|
+
}
|
|
43
14
|
}
|
|
44
15
|
inFlightRequests = /* @__PURE__ */ new Set();
|
|
16
|
+
/** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
|
|
17
|
+
get authKey() {
|
|
18
|
+
return this.config.secretKey ?? this.config.publicKey ?? "";
|
|
19
|
+
}
|
|
45
20
|
/**
|
|
46
21
|
* Send a request with automatic retry and exponential backoff.
|
|
47
22
|
* Retries up to `maxRetries` times on network errors and 5xx responses.
|
|
@@ -55,7 +30,7 @@ var Transport = class {
|
|
|
55
30
|
method: "POST",
|
|
56
31
|
headers: {
|
|
57
32
|
"Content-Type": "application/json",
|
|
58
|
-
Authorization: `Bearer ${this.
|
|
33
|
+
Authorization: `Bearer ${this.authKey}`,
|
|
59
34
|
"x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
|
|
60
35
|
},
|
|
61
36
|
body: JSON.stringify(body)
|
|
@@ -79,9 +54,7 @@ var Transport = class {
|
|
|
79
54
|
await this.sleep(this.jitter(baseDelays[attempt]));
|
|
80
55
|
continue;
|
|
81
56
|
}
|
|
82
|
-
console.warn(
|
|
83
|
-
`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`
|
|
84
|
-
);
|
|
57
|
+
console.warn(`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`);
|
|
85
58
|
}
|
|
86
59
|
}
|
|
87
60
|
}
|
|
@@ -102,7 +75,6 @@ var Transport = class {
|
|
|
102
75
|
const p = this.sendWithRetry(
|
|
103
76
|
`${this.config.endpoint}/ingest/logs`,
|
|
104
77
|
{
|
|
105
|
-
product: this.config.product,
|
|
106
78
|
service: this.config.service,
|
|
107
79
|
environment: this.config.environment,
|
|
108
80
|
logs
|
|
@@ -117,7 +89,6 @@ var Transport = class {
|
|
|
117
89
|
`${this.config.endpoint}/ingest/errors`,
|
|
118
90
|
{
|
|
119
91
|
...error,
|
|
120
|
-
product: this.config.product,
|
|
121
92
|
service: this.config.service,
|
|
122
93
|
environment: this.config.environment
|
|
123
94
|
},
|
|
@@ -131,7 +102,6 @@ var Transport = class {
|
|
|
131
102
|
`${this.config.endpoint}/ingest/traces`,
|
|
132
103
|
{
|
|
133
104
|
...span,
|
|
134
|
-
product: this.config.product,
|
|
135
105
|
service: this.config.service,
|
|
136
106
|
environment: this.config.environment
|
|
137
107
|
},
|
|
@@ -145,7 +115,6 @@ var Transport = class {
|
|
|
145
115
|
`${this.config.endpoint}/ingest/llm`,
|
|
146
116
|
{
|
|
147
117
|
...report,
|
|
148
|
-
product: this.config.product,
|
|
149
118
|
service: this.config.service,
|
|
150
119
|
environment: this.config.environment
|
|
151
120
|
},
|
|
@@ -169,6 +138,40 @@ var Transport = class {
|
|
|
169
138
|
}
|
|
170
139
|
};
|
|
171
140
|
|
|
141
|
+
// src/batcher.ts
|
|
142
|
+
var Batcher = class {
|
|
143
|
+
constructor(config, onFlush) {
|
|
144
|
+
this.onFlush = onFlush;
|
|
145
|
+
this.batchSize = config.batchSize ?? 50;
|
|
146
|
+
this.flushIntervalMs = config.flushIntervalMs ?? 5e3;
|
|
147
|
+
this.startTimer();
|
|
148
|
+
}
|
|
149
|
+
buffer = [];
|
|
150
|
+
timer = null;
|
|
151
|
+
batchSize;
|
|
152
|
+
flushIntervalMs;
|
|
153
|
+
add(entry) {
|
|
154
|
+
this.buffer.push(entry);
|
|
155
|
+
if (this.buffer.length >= this.batchSize) {
|
|
156
|
+
this.flush();
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
flush() {
|
|
160
|
+
if (this.buffer.length === 0) return;
|
|
161
|
+
const entries = [...this.buffer];
|
|
162
|
+
this.buffer = [];
|
|
163
|
+
this.onFlush(entries);
|
|
164
|
+
}
|
|
165
|
+
startTimer() {
|
|
166
|
+
this.timer = setInterval(() => this.flush(), this.flushIntervalMs);
|
|
167
|
+
}
|
|
168
|
+
async destroy() {
|
|
169
|
+
if (this.timer) clearInterval(this.timer);
|
|
170
|
+
this.timer = null;
|
|
171
|
+
this.flush();
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
172
175
|
// src/state.ts
|
|
173
176
|
function createLoggerState(maxBreadcrumbs) {
|
|
174
177
|
return {
|
|
@@ -179,6 +182,17 @@ function createLoggerState(maxBreadcrumbs) {
|
|
|
179
182
|
maxBreadcrumbs
|
|
180
183
|
};
|
|
181
184
|
}
|
|
185
|
+
function cloneState(state) {
|
|
186
|
+
return {
|
|
187
|
+
user: state.user ? { ...state.user } : null,
|
|
188
|
+
tags: { ...state.tags },
|
|
189
|
+
contexts: Object.fromEntries(
|
|
190
|
+
Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])
|
|
191
|
+
),
|
|
192
|
+
breadcrumbs: [...state.breadcrumbs],
|
|
193
|
+
maxBreadcrumbs: state.maxBreadcrumbs
|
|
194
|
+
};
|
|
195
|
+
}
|
|
182
196
|
function addBreadcrumb(state, breadcrumb) {
|
|
183
197
|
state.breadcrumbs.push(breadcrumb);
|
|
184
198
|
if (state.breadcrumbs.length > state.maxBreadcrumbs) {
|
|
@@ -187,11 +201,33 @@ function addBreadcrumb(state, breadcrumb) {
|
|
|
187
201
|
}
|
|
188
202
|
|
|
189
203
|
// src/logger.ts
|
|
204
|
+
var LOG_LEVEL_VALUES = {
|
|
205
|
+
debug: 0,
|
|
206
|
+
info: 1,
|
|
207
|
+
warn: 2,
|
|
208
|
+
error: 3
|
|
209
|
+
};
|
|
190
210
|
function generateId() {
|
|
191
211
|
const bytes = new Uint8Array(8);
|
|
192
212
|
crypto.getRandomValues(bytes);
|
|
193
213
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
194
214
|
}
|
|
215
|
+
function generateTraceId() {
|
|
216
|
+
const bytes = new Uint8Array(16);
|
|
217
|
+
crypto.getRandomValues(bytes);
|
|
218
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
219
|
+
}
|
|
220
|
+
function parseTraceparent(header) {
|
|
221
|
+
const parts = header.trim().split("-");
|
|
222
|
+
if (parts.length !== 4) return null;
|
|
223
|
+
const [version, traceId, parentId, flags] = parts;
|
|
224
|
+
if (version !== "00") return null;
|
|
225
|
+
if (traceId.length !== 32 || !/^[0-9a-f]{32}$/.test(traceId)) return null;
|
|
226
|
+
if (parentId.length !== 16 || !/^[0-9a-f]{16}$/.test(parentId)) return null;
|
|
227
|
+
if (flags.length !== 2 || !/^[0-9a-f]{2}$/.test(flags)) return null;
|
|
228
|
+
if (/^0+$/.test(traceId) || /^0+$/.test(parentId)) return null;
|
|
229
|
+
return { traceId, parentId, flags };
|
|
230
|
+
}
|
|
195
231
|
var _originalConsole = {
|
|
196
232
|
log: console.log,
|
|
197
233
|
info: console.info,
|
|
@@ -202,6 +238,7 @@ var _originalConsole = {
|
|
|
202
238
|
var Logger = class _Logger {
|
|
203
239
|
batcher;
|
|
204
240
|
transport;
|
|
241
|
+
effectiveLevel;
|
|
205
242
|
contextName;
|
|
206
243
|
config;
|
|
207
244
|
state;
|
|
@@ -211,6 +248,17 @@ var Logger = class _Logger {
|
|
|
211
248
|
this.contextName = contextName;
|
|
212
249
|
this.requestMeta = requestMeta;
|
|
213
250
|
this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
|
|
251
|
+
if (!config.secretKey && !config.publicKey) {
|
|
252
|
+
_originalConsole.error(
|
|
253
|
+
"[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be authenticated."
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
if (!config.endpoint) {
|
|
257
|
+
_originalConsole.error(
|
|
258
|
+
"[@deeptracer/core] No `endpoint` provided. Events will not be sent."
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
|
|
214
262
|
this.transport = new Transport(config);
|
|
215
263
|
this.batcher = new Batcher(
|
|
216
264
|
{ batchSize: config.batchSize, flushIntervalMs: config.flushIntervalMs },
|
|
@@ -224,7 +272,8 @@ var Logger = class _Logger {
|
|
|
224
272
|
// ---------------------------------------------------------------------------
|
|
225
273
|
/**
|
|
226
274
|
* Set the current user context. Attached to all subsequent logs, errors, spans, and LLM reports.
|
|
227
|
-
*
|
|
275
|
+
* Only affects this logger instance — child loggers created via `withContext()` or `forRequest()`
|
|
276
|
+
* have their own independent state.
|
|
228
277
|
*
|
|
229
278
|
* @example
|
|
230
279
|
* ```ts
|
|
@@ -346,26 +395,13 @@ var Logger = class _Logger {
|
|
|
346
395
|
};
|
|
347
396
|
}
|
|
348
397
|
metadata = this.mergeStateMetadata(metadata);
|
|
349
|
-
const
|
|
350
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
351
|
-
level,
|
|
352
|
-
message,
|
|
353
|
-
metadata,
|
|
354
|
-
context: this.contextName,
|
|
355
|
-
trace_id: this.requestMeta?.trace_id,
|
|
356
|
-
span_id: this.requestMeta?.span_id,
|
|
357
|
-
request_id: this.requestMeta?.request_id,
|
|
358
|
-
vercel_id: this.requestMeta?.vercel_id
|
|
359
|
-
};
|
|
360
|
-
const hookResult = this.applyBeforeSend({ type: "log", data: entry });
|
|
361
|
-
if (hookResult === null) return;
|
|
362
|
-
const finalEntry = hookResult.data;
|
|
398
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
363
399
|
if (this.config.debug) {
|
|
364
400
|
const prefix = this.contextName ? `[${this.contextName}]` : "";
|
|
365
401
|
const lvl = level.toUpperCase().padEnd(5);
|
|
366
402
|
const consoleFn = level === "error" ? _originalConsole.error : level === "warn" ? _originalConsole.warn : level === "debug" ? _originalConsole.debug : _originalConsole.log;
|
|
367
|
-
if (
|
|
368
|
-
consoleFn(`${lvl} ${prefix} ${message}`,
|
|
403
|
+
if (metadata) {
|
|
404
|
+
consoleFn(`${lvl} ${prefix} ${message}`, metadata);
|
|
369
405
|
} else {
|
|
370
406
|
consoleFn(`${lvl} ${prefix} ${message}`);
|
|
371
407
|
}
|
|
@@ -373,9 +409,23 @@ var Logger = class _Logger {
|
|
|
373
409
|
addBreadcrumb(this.state, {
|
|
374
410
|
type: "log",
|
|
375
411
|
message: `[${level}] ${message}`,
|
|
376
|
-
timestamp
|
|
412
|
+
timestamp
|
|
377
413
|
});
|
|
378
|
-
this.
|
|
414
|
+
if (LOG_LEVEL_VALUES[level] < this.effectiveLevel) return;
|
|
415
|
+
const entry = {
|
|
416
|
+
timestamp,
|
|
417
|
+
level,
|
|
418
|
+
message,
|
|
419
|
+
metadata,
|
|
420
|
+
context: this.contextName,
|
|
421
|
+
trace_id: this.requestMeta?.trace_id,
|
|
422
|
+
span_id: this.requestMeta?.span_id,
|
|
423
|
+
request_id: this.requestMeta?.request_id,
|
|
424
|
+
vercel_id: this.requestMeta?.vercel_id
|
|
425
|
+
};
|
|
426
|
+
const hookResult = this.applyBeforeSend({ type: "log", data: entry });
|
|
427
|
+
if (hookResult === null) return;
|
|
428
|
+
this.batcher.add(hookResult.data);
|
|
379
429
|
}
|
|
380
430
|
/** Log a debug message. */
|
|
381
431
|
debug(message, dataOrError, error) {
|
|
@@ -396,22 +446,37 @@ var Logger = class _Logger {
|
|
|
396
446
|
// ---------------------------------------------------------------------------
|
|
397
447
|
// Child loggers
|
|
398
448
|
// ---------------------------------------------------------------------------
|
|
399
|
-
/** Create a context-scoped logger. All logs include the context name.
|
|
449
|
+
/** Create a context-scoped logger. All logs include the context name. Gets an independent copy of state. */
|
|
400
450
|
withContext(name) {
|
|
401
|
-
return new _Logger(this.config, name, this.requestMeta, this.state);
|
|
451
|
+
return new _Logger(this.config, name, this.requestMeta, cloneState(this.state));
|
|
402
452
|
}
|
|
403
|
-
/** Create a request-scoped logger that extracts trace context from headers.
|
|
453
|
+
/** Create a request-scoped logger that extracts trace context from headers. Gets an independent copy of state. */
|
|
404
454
|
forRequest(request) {
|
|
405
|
-
|
|
455
|
+
let traceId;
|
|
456
|
+
let spanId;
|
|
457
|
+
const traceparent = request.headers.get("traceparent");
|
|
458
|
+
if (traceparent) {
|
|
459
|
+
const parsed = parseTraceparent(traceparent);
|
|
460
|
+
if (parsed) {
|
|
461
|
+
traceId = parsed.traceId;
|
|
462
|
+
spanId = parsed.parentId;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
traceId = traceId || request.headers.get("x-trace-id") || void 0;
|
|
466
|
+
spanId = spanId || request.headers.get("x-span-id") || void 0;
|
|
406
467
|
const requestId = request.headers.get("x-request-id") || void 0;
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
468
|
+
const vercelId = request.headers.get("x-vercel-id") || void 0;
|
|
469
|
+
return new _Logger(
|
|
470
|
+
this.config,
|
|
471
|
+
this.contextName,
|
|
472
|
+
{
|
|
473
|
+
trace_id: traceId,
|
|
474
|
+
span_id: spanId,
|
|
475
|
+
request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
|
|
476
|
+
vercel_id: vercelId
|
|
477
|
+
},
|
|
478
|
+
cloneState(this.state)
|
|
479
|
+
);
|
|
415
480
|
}
|
|
416
481
|
// ---------------------------------------------------------------------------
|
|
417
482
|
// Error capture
|
|
@@ -430,7 +495,8 @@ var Logger = class _Logger {
|
|
|
430
495
|
const enrichedContext = { ...context?.context };
|
|
431
496
|
if (this.state.user) enrichedContext.user = this.state.user;
|
|
432
497
|
if (Object.keys(this.state.tags).length > 0) enrichedContext._tags = { ...this.state.tags };
|
|
433
|
-
if (Object.keys(this.state.contexts).length > 0)
|
|
498
|
+
if (Object.keys(this.state.contexts).length > 0)
|
|
499
|
+
enrichedContext._contexts = { ...this.state.contexts };
|
|
434
500
|
const report = {
|
|
435
501
|
error_message: err.message,
|
|
436
502
|
stack_trace: err.stack || "",
|
|
@@ -511,7 +577,7 @@ var Logger = class _Logger {
|
|
|
511
577
|
}
|
|
512
578
|
/** Start a span with manual lifecycle. You must call span.end(). */
|
|
513
579
|
startInactiveSpan(operation) {
|
|
514
|
-
const traceId = this.requestMeta?.trace_id ||
|
|
580
|
+
const traceId = this.requestMeta?.trace_id || generateTraceId();
|
|
515
581
|
const parentSpanId = this.requestMeta?.span_id || "";
|
|
516
582
|
const spanId = generateId();
|
|
517
583
|
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -551,10 +617,16 @@ var Logger = class _Logger {
|
|
|
551
617
|
const childLogger = new _Logger(this.config, this.contextName, childMeta, this.state);
|
|
552
618
|
return childLogger.startInactiveSpan(childOp);
|
|
553
619
|
},
|
|
554
|
-
getHeaders: () =>
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
620
|
+
getHeaders: () => {
|
|
621
|
+
const headers = {
|
|
622
|
+
"x-trace-id": traceId,
|
|
623
|
+
"x-span-id": spanId
|
|
624
|
+
};
|
|
625
|
+
if (/^[0-9a-f]{32}$/.test(traceId)) {
|
|
626
|
+
headers.traceparent = `00-${traceId}-${spanId}-01`;
|
|
627
|
+
}
|
|
628
|
+
return headers;
|
|
629
|
+
}
|
|
558
630
|
};
|
|
559
631
|
return span;
|
|
560
632
|
}
|
|
@@ -595,6 +667,8 @@ function createLogger(config) {
|
|
|
595
667
|
export {
|
|
596
668
|
SDK_VERSION,
|
|
597
669
|
SDK_NAME,
|
|
670
|
+
Transport,
|
|
671
|
+
parseTraceparent,
|
|
598
672
|
_originalConsole,
|
|
599
673
|
Logger,
|
|
600
674
|
createLogger
|
package/dist/index.cjs
CHANGED
|
@@ -62,15 +62,24 @@ var Batcher = class {
|
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
// src/version.ts
|
|
65
|
-
var SDK_VERSION = "0.
|
|
65
|
+
var SDK_VERSION = "0.4.1";
|
|
66
66
|
var SDK_NAME = "core";
|
|
67
67
|
|
|
68
68
|
// src/transport.ts
|
|
69
69
|
var Transport = class {
|
|
70
70
|
constructor(config) {
|
|
71
71
|
this.config = config;
|
|
72
|
+
if (config.secretKey?.startsWith("dt_secret_") && typeof globalThis.window !== "undefined") {
|
|
73
|
+
console.error(
|
|
74
|
+
"[@deeptracer/core] WARNING: `secretKey` (dt_secret_...) detected in a browser bundle. This exposes your server key to end users. Use `publicKey` (dt_public_...) for client-side code."
|
|
75
|
+
);
|
|
76
|
+
}
|
|
72
77
|
}
|
|
73
78
|
inFlightRequests = /* @__PURE__ */ new Set();
|
|
79
|
+
/** Resolve the auth key: prefer secretKey (server), fall back to publicKey (client). */
|
|
80
|
+
get authKey() {
|
|
81
|
+
return this.config.secretKey ?? this.config.publicKey ?? "";
|
|
82
|
+
}
|
|
74
83
|
/**
|
|
75
84
|
* Send a request with automatic retry and exponential backoff.
|
|
76
85
|
* Retries up to `maxRetries` times on network errors and 5xx responses.
|
|
@@ -84,7 +93,7 @@ var Transport = class {
|
|
|
84
93
|
method: "POST",
|
|
85
94
|
headers: {
|
|
86
95
|
"Content-Type": "application/json",
|
|
87
|
-
Authorization: `Bearer ${this.
|
|
96
|
+
Authorization: `Bearer ${this.authKey}`,
|
|
88
97
|
"x-deeptracer-sdk": `${SDK_NAME}/${SDK_VERSION}`
|
|
89
98
|
},
|
|
90
99
|
body: JSON.stringify(body)
|
|
@@ -108,9 +117,7 @@ var Transport = class {
|
|
|
108
117
|
await this.sleep(this.jitter(baseDelays[attempt]));
|
|
109
118
|
continue;
|
|
110
119
|
}
|
|
111
|
-
console.warn(
|
|
112
|
-
`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`
|
|
113
|
-
);
|
|
120
|
+
console.warn(`[@deeptracer/core] Failed to send ${label} (exhausted ${maxRetries} retries)`);
|
|
114
121
|
}
|
|
115
122
|
}
|
|
116
123
|
}
|
|
@@ -131,7 +138,6 @@ var Transport = class {
|
|
|
131
138
|
const p = this.sendWithRetry(
|
|
132
139
|
`${this.config.endpoint}/ingest/logs`,
|
|
133
140
|
{
|
|
134
|
-
product: this.config.product,
|
|
135
141
|
service: this.config.service,
|
|
136
142
|
environment: this.config.environment,
|
|
137
143
|
logs
|
|
@@ -146,7 +152,6 @@ var Transport = class {
|
|
|
146
152
|
`${this.config.endpoint}/ingest/errors`,
|
|
147
153
|
{
|
|
148
154
|
...error,
|
|
149
|
-
product: this.config.product,
|
|
150
155
|
service: this.config.service,
|
|
151
156
|
environment: this.config.environment
|
|
152
157
|
},
|
|
@@ -160,7 +165,6 @@ var Transport = class {
|
|
|
160
165
|
`${this.config.endpoint}/ingest/traces`,
|
|
161
166
|
{
|
|
162
167
|
...span,
|
|
163
|
-
product: this.config.product,
|
|
164
168
|
service: this.config.service,
|
|
165
169
|
environment: this.config.environment
|
|
166
170
|
},
|
|
@@ -174,7 +178,6 @@ var Transport = class {
|
|
|
174
178
|
`${this.config.endpoint}/ingest/llm`,
|
|
175
179
|
{
|
|
176
180
|
...report,
|
|
177
|
-
product: this.config.product,
|
|
178
181
|
service: this.config.service,
|
|
179
182
|
environment: this.config.environment
|
|
180
183
|
},
|
|
@@ -208,6 +211,17 @@ function createLoggerState(maxBreadcrumbs) {
|
|
|
208
211
|
maxBreadcrumbs
|
|
209
212
|
};
|
|
210
213
|
}
|
|
214
|
+
function cloneState(state) {
|
|
215
|
+
return {
|
|
216
|
+
user: state.user ? { ...state.user } : null,
|
|
217
|
+
tags: { ...state.tags },
|
|
218
|
+
contexts: Object.fromEntries(
|
|
219
|
+
Object.entries(state.contexts).map(([k, v]) => [k, { ...v }])
|
|
220
|
+
),
|
|
221
|
+
breadcrumbs: [...state.breadcrumbs],
|
|
222
|
+
maxBreadcrumbs: state.maxBreadcrumbs
|
|
223
|
+
};
|
|
224
|
+
}
|
|
211
225
|
function addBreadcrumb(state, breadcrumb) {
|
|
212
226
|
state.breadcrumbs.push(breadcrumb);
|
|
213
227
|
if (state.breadcrumbs.length > state.maxBreadcrumbs) {
|
|
@@ -216,11 +230,33 @@ function addBreadcrumb(state, breadcrumb) {
|
|
|
216
230
|
}
|
|
217
231
|
|
|
218
232
|
// src/logger.ts
|
|
233
|
+
var LOG_LEVEL_VALUES = {
|
|
234
|
+
debug: 0,
|
|
235
|
+
info: 1,
|
|
236
|
+
warn: 2,
|
|
237
|
+
error: 3
|
|
238
|
+
};
|
|
219
239
|
function generateId() {
|
|
220
240
|
const bytes = new Uint8Array(8);
|
|
221
241
|
crypto.getRandomValues(bytes);
|
|
222
242
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
223
243
|
}
|
|
244
|
+
function generateTraceId() {
|
|
245
|
+
const bytes = new Uint8Array(16);
|
|
246
|
+
crypto.getRandomValues(bytes);
|
|
247
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
248
|
+
}
|
|
249
|
+
function parseTraceparent(header) {
|
|
250
|
+
const parts = header.trim().split("-");
|
|
251
|
+
if (parts.length !== 4) return null;
|
|
252
|
+
const [version, traceId, parentId, flags] = parts;
|
|
253
|
+
if (version !== "00") return null;
|
|
254
|
+
if (traceId.length !== 32 || !/^[0-9a-f]{32}$/.test(traceId)) return null;
|
|
255
|
+
if (parentId.length !== 16 || !/^[0-9a-f]{16}$/.test(parentId)) return null;
|
|
256
|
+
if (flags.length !== 2 || !/^[0-9a-f]{2}$/.test(flags)) return null;
|
|
257
|
+
if (/^0+$/.test(traceId) || /^0+$/.test(parentId)) return null;
|
|
258
|
+
return { traceId, parentId, flags };
|
|
259
|
+
}
|
|
224
260
|
var _originalConsole = {
|
|
225
261
|
log: console.log,
|
|
226
262
|
info: console.info,
|
|
@@ -231,6 +267,7 @@ var _originalConsole = {
|
|
|
231
267
|
var Logger = class _Logger {
|
|
232
268
|
batcher;
|
|
233
269
|
transport;
|
|
270
|
+
effectiveLevel;
|
|
234
271
|
contextName;
|
|
235
272
|
config;
|
|
236
273
|
state;
|
|
@@ -240,6 +277,17 @@ var Logger = class _Logger {
|
|
|
240
277
|
this.contextName = contextName;
|
|
241
278
|
this.requestMeta = requestMeta;
|
|
242
279
|
this.state = state ?? createLoggerState(config.maxBreadcrumbs ?? 20);
|
|
280
|
+
if (!config.secretKey && !config.publicKey) {
|
|
281
|
+
_originalConsole.error(
|
|
282
|
+
"[@deeptracer/core] No `secretKey` or `publicKey` provided. Events will not be authenticated."
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (!config.endpoint) {
|
|
286
|
+
_originalConsole.error(
|
|
287
|
+
"[@deeptracer/core] No `endpoint` provided. Events will not be sent."
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
this.effectiveLevel = LOG_LEVEL_VALUES[config.level ?? (config.environment === "production" ? "info" : "debug")];
|
|
243
291
|
this.transport = new Transport(config);
|
|
244
292
|
this.batcher = new Batcher(
|
|
245
293
|
{ batchSize: config.batchSize, flushIntervalMs: config.flushIntervalMs },
|
|
@@ -253,7 +301,8 @@ var Logger = class _Logger {
|
|
|
253
301
|
// ---------------------------------------------------------------------------
|
|
254
302
|
/**
|
|
255
303
|
* Set the current user context. Attached to all subsequent logs, errors, spans, and LLM reports.
|
|
256
|
-
*
|
|
304
|
+
* Only affects this logger instance — child loggers created via `withContext()` or `forRequest()`
|
|
305
|
+
* have their own independent state.
|
|
257
306
|
*
|
|
258
307
|
* @example
|
|
259
308
|
* ```ts
|
|
@@ -375,26 +424,13 @@ var Logger = class _Logger {
|
|
|
375
424
|
};
|
|
376
425
|
}
|
|
377
426
|
metadata = this.mergeStateMetadata(metadata);
|
|
378
|
-
const
|
|
379
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
380
|
-
level,
|
|
381
|
-
message,
|
|
382
|
-
metadata,
|
|
383
|
-
context: this.contextName,
|
|
384
|
-
trace_id: this.requestMeta?.trace_id,
|
|
385
|
-
span_id: this.requestMeta?.span_id,
|
|
386
|
-
request_id: this.requestMeta?.request_id,
|
|
387
|
-
vercel_id: this.requestMeta?.vercel_id
|
|
388
|
-
};
|
|
389
|
-
const hookResult = this.applyBeforeSend({ type: "log", data: entry });
|
|
390
|
-
if (hookResult === null) return;
|
|
391
|
-
const finalEntry = hookResult.data;
|
|
427
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
392
428
|
if (this.config.debug) {
|
|
393
429
|
const prefix = this.contextName ? `[${this.contextName}]` : "";
|
|
394
430
|
const lvl = level.toUpperCase().padEnd(5);
|
|
395
431
|
const consoleFn = level === "error" ? _originalConsole.error : level === "warn" ? _originalConsole.warn : level === "debug" ? _originalConsole.debug : _originalConsole.log;
|
|
396
|
-
if (
|
|
397
|
-
consoleFn(`${lvl} ${prefix} ${message}`,
|
|
432
|
+
if (metadata) {
|
|
433
|
+
consoleFn(`${lvl} ${prefix} ${message}`, metadata);
|
|
398
434
|
} else {
|
|
399
435
|
consoleFn(`${lvl} ${prefix} ${message}`);
|
|
400
436
|
}
|
|
@@ -402,9 +438,23 @@ var Logger = class _Logger {
|
|
|
402
438
|
addBreadcrumb(this.state, {
|
|
403
439
|
type: "log",
|
|
404
440
|
message: `[${level}] ${message}`,
|
|
405
|
-
timestamp
|
|
441
|
+
timestamp
|
|
406
442
|
});
|
|
407
|
-
this.
|
|
443
|
+
if (LOG_LEVEL_VALUES[level] < this.effectiveLevel) return;
|
|
444
|
+
const entry = {
|
|
445
|
+
timestamp,
|
|
446
|
+
level,
|
|
447
|
+
message,
|
|
448
|
+
metadata,
|
|
449
|
+
context: this.contextName,
|
|
450
|
+
trace_id: this.requestMeta?.trace_id,
|
|
451
|
+
span_id: this.requestMeta?.span_id,
|
|
452
|
+
request_id: this.requestMeta?.request_id,
|
|
453
|
+
vercel_id: this.requestMeta?.vercel_id
|
|
454
|
+
};
|
|
455
|
+
const hookResult = this.applyBeforeSend({ type: "log", data: entry });
|
|
456
|
+
if (hookResult === null) return;
|
|
457
|
+
this.batcher.add(hookResult.data);
|
|
408
458
|
}
|
|
409
459
|
/** Log a debug message. */
|
|
410
460
|
debug(message, dataOrError, error) {
|
|
@@ -425,22 +475,37 @@ var Logger = class _Logger {
|
|
|
425
475
|
// ---------------------------------------------------------------------------
|
|
426
476
|
// Child loggers
|
|
427
477
|
// ---------------------------------------------------------------------------
|
|
428
|
-
/** Create a context-scoped logger. All logs include the context name.
|
|
478
|
+
/** Create a context-scoped logger. All logs include the context name. Gets an independent copy of state. */
|
|
429
479
|
withContext(name) {
|
|
430
|
-
return new _Logger(this.config, name, this.requestMeta, this.state);
|
|
480
|
+
return new _Logger(this.config, name, this.requestMeta, cloneState(this.state));
|
|
431
481
|
}
|
|
432
|
-
/** Create a request-scoped logger that extracts trace context from headers.
|
|
482
|
+
/** Create a request-scoped logger that extracts trace context from headers. Gets an independent copy of state. */
|
|
433
483
|
forRequest(request) {
|
|
434
|
-
|
|
484
|
+
let traceId;
|
|
485
|
+
let spanId;
|
|
486
|
+
const traceparent = request.headers.get("traceparent");
|
|
487
|
+
if (traceparent) {
|
|
488
|
+
const parsed = parseTraceparent(traceparent);
|
|
489
|
+
if (parsed) {
|
|
490
|
+
traceId = parsed.traceId;
|
|
491
|
+
spanId = parsed.parentId;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
traceId = traceId || request.headers.get("x-trace-id") || void 0;
|
|
495
|
+
spanId = spanId || request.headers.get("x-span-id") || void 0;
|
|
435
496
|
const requestId = request.headers.get("x-request-id") || void 0;
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
497
|
+
const vercelId = request.headers.get("x-vercel-id") || void 0;
|
|
498
|
+
return new _Logger(
|
|
499
|
+
this.config,
|
|
500
|
+
this.contextName,
|
|
501
|
+
{
|
|
502
|
+
trace_id: traceId,
|
|
503
|
+
span_id: spanId,
|
|
504
|
+
request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
|
|
505
|
+
vercel_id: vercelId
|
|
506
|
+
},
|
|
507
|
+
cloneState(this.state)
|
|
508
|
+
);
|
|
444
509
|
}
|
|
445
510
|
// ---------------------------------------------------------------------------
|
|
446
511
|
// Error capture
|
|
@@ -459,7 +524,8 @@ var Logger = class _Logger {
|
|
|
459
524
|
const enrichedContext = { ...context?.context };
|
|
460
525
|
if (this.state.user) enrichedContext.user = this.state.user;
|
|
461
526
|
if (Object.keys(this.state.tags).length > 0) enrichedContext._tags = { ...this.state.tags };
|
|
462
|
-
if (Object.keys(this.state.contexts).length > 0)
|
|
527
|
+
if (Object.keys(this.state.contexts).length > 0)
|
|
528
|
+
enrichedContext._contexts = { ...this.state.contexts };
|
|
463
529
|
const report = {
|
|
464
530
|
error_message: err.message,
|
|
465
531
|
stack_trace: err.stack || "",
|
|
@@ -540,7 +606,7 @@ var Logger = class _Logger {
|
|
|
540
606
|
}
|
|
541
607
|
/** Start a span with manual lifecycle. You must call span.end(). */
|
|
542
608
|
startInactiveSpan(operation) {
|
|
543
|
-
const traceId = this.requestMeta?.trace_id ||
|
|
609
|
+
const traceId = this.requestMeta?.trace_id || generateTraceId();
|
|
544
610
|
const parentSpanId = this.requestMeta?.span_id || "";
|
|
545
611
|
const spanId = generateId();
|
|
546
612
|
const startTime = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -580,10 +646,16 @@ var Logger = class _Logger {
|
|
|
580
646
|
const childLogger = new _Logger(this.config, this.contextName, childMeta, this.state);
|
|
581
647
|
return childLogger.startInactiveSpan(childOp);
|
|
582
648
|
},
|
|
583
|
-
getHeaders: () =>
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
649
|
+
getHeaders: () => {
|
|
650
|
+
const headers = {
|
|
651
|
+
"x-trace-id": traceId,
|
|
652
|
+
"x-span-id": spanId
|
|
653
|
+
};
|
|
654
|
+
if (/^[0-9a-f]{32}$/.test(traceId)) {
|
|
655
|
+
headers.traceparent = `00-${traceId}-${spanId}-01`;
|
|
656
|
+
}
|
|
657
|
+
return headers;
|
|
658
|
+
}
|
|
587
659
|
};
|
|
588
660
|
return span;
|
|
589
661
|
}
|