@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.
@@ -1,47 +1,22 @@
1
1
  // src/version.ts
2
- var SDK_VERSION = "0.3.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.config.apiKey}`,
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
- * Shared across all child loggers (withContext, forRequest).
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 entry = {
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 (finalEntry.metadata) {
368
- consoleFn(`${lvl} ${prefix} ${message}`, finalEntry.metadata);
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: entry.timestamp
412
+ timestamp
377
413
  });
378
- this.batcher.add(finalEntry);
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. Shares state with parent. */
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. Shares state with parent. */
453
+ /** Create a request-scoped logger that extracts trace context from headers. Gets an independent copy of state. */
404
454
  forRequest(request) {
405
- const vercelId = request.headers.get("x-vercel-id") || void 0;
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 traceId = request.headers.get("x-trace-id") || void 0;
408
- const spanId = request.headers.get("x-span-id") || void 0;
409
- return new _Logger(this.config, this.contextName, {
410
- trace_id: traceId,
411
- span_id: spanId,
412
- request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
413
- vercel_id: vercelId
414
- }, this.state);
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) enrichedContext._contexts = { ...this.state.contexts };
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 || generateId();
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
- "x-trace-id": traceId,
556
- "x-span-id": spanId
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.3.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.config.apiKey}`,
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
- * Shared across all child loggers (withContext, forRequest).
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 entry = {
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 (finalEntry.metadata) {
397
- consoleFn(`${lvl} ${prefix} ${message}`, finalEntry.metadata);
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: entry.timestamp
441
+ timestamp
406
442
  });
407
- this.batcher.add(finalEntry);
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. Shares state with parent. */
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. Shares state with parent. */
482
+ /** Create a request-scoped logger that extracts trace context from headers. Gets an independent copy of state. */
433
483
  forRequest(request) {
434
- const vercelId = request.headers.get("x-vercel-id") || void 0;
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 traceId = request.headers.get("x-trace-id") || void 0;
437
- const spanId = request.headers.get("x-span-id") || void 0;
438
- return new _Logger(this.config, this.contextName, {
439
- trace_id: traceId,
440
- span_id: spanId,
441
- request_id: requestId || (vercelId ? vercelId.split("::").pop() : void 0),
442
- vercel_id: vercelId
443
- }, this.state);
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) enrichedContext._contexts = { ...this.state.contexts };
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 || generateId();
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
- "x-trace-id": traceId,
585
- "x-span-id": spanId
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
  }