@tanngo11/log 0.3.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/CONTRACT.md +135 -0
  3. package/LICENSE +21 -0
  4. package/README.md +291 -0
  5. package/dist/bullmq.cjs +108 -0
  6. package/dist/bullmq.cjs.map +1 -0
  7. package/dist/bullmq.d.cts +14 -0
  8. package/dist/bullmq.d.ts +14 -0
  9. package/dist/bullmq.js +29 -0
  10. package/dist/bullmq.js.map +1 -0
  11. package/dist/chunk-BP264YAM.js +72 -0
  12. package/dist/chunk-BP264YAM.js.map +1 -0
  13. package/dist/chunk-BVKAY2OK.js +53 -0
  14. package/dist/chunk-BVKAY2OK.js.map +1 -0
  15. package/dist/chunk-DO5HNNTP.js +66 -0
  16. package/dist/chunk-DO5HNNTP.js.map +1 -0
  17. package/dist/chunk-KD5H5G6Y.js +209 -0
  18. package/dist/chunk-KD5H5G6Y.js.map +1 -0
  19. package/dist/chunk-KLBL7QEN.js +29 -0
  20. package/dist/chunk-KLBL7QEN.js.map +1 -0
  21. package/dist/chunk-QMC6BWGK.js +203 -0
  22. package/dist/chunk-QMC6BWGK.js.map +1 -0
  23. package/dist/chunk-UXKPPF4G.js +41 -0
  24. package/dist/chunk-UXKPPF4G.js.map +1 -0
  25. package/dist/express.cjs +316 -0
  26. package/dist/express.cjs.map +1 -0
  27. package/dist/express.d.cts +10 -0
  28. package/dist/express.d.ts +10 -0
  29. package/dist/express.js +12 -0
  30. package/dist/express.js.map +1 -0
  31. package/dist/fastify.cjs +309 -0
  32. package/dist/fastify.cjs.map +1 -0
  33. package/dist/fastify.d.cts +10 -0
  34. package/dist/fastify.d.ts +10 -0
  35. package/dist/fastify.js +10 -0
  36. package/dist/fastify.js.map +1 -0
  37. package/dist/http-BI6ARZlD.d.ts +25 -0
  38. package/dist/http-BXAiU-rN.d.cts +25 -0
  39. package/dist/index.cjs +677 -0
  40. package/dist/index.cjs.map +1 -0
  41. package/dist/index.d.cts +69 -0
  42. package/dist/index.d.ts +69 -0
  43. package/dist/index.js +214 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/logger-CFMTzjsm.d.cts +47 -0
  46. package/dist/logger-CFMTzjsm.d.ts +47 -0
  47. package/dist/nest.cjs +458 -0
  48. package/dist/nest.cjs.map +1 -0
  49. package/dist/nest.d.cts +41 -0
  50. package/dist/nest.d.ts +41 -0
  51. package/dist/nest.js +107 -0
  52. package/dist/nest.js.map +1 -0
  53. package/dist/next-edge.cjs +44 -0
  54. package/dist/next-edge.cjs.map +1 -0
  55. package/dist/next-edge.d.cts +7 -0
  56. package/dist/next-edge.d.ts +7 -0
  57. package/dist/next-edge.js +16 -0
  58. package/dist/next-edge.js.map +1 -0
  59. package/dist/next.cjs +423 -0
  60. package/dist/next.cjs.map +1 -0
  61. package/dist/next.d.cts +36 -0
  62. package/dist/next.d.ts +36 -0
  63. package/dist/next.js +138 -0
  64. package/dist/next.js.map +1 -0
  65. package/dist/prisma.cjs +63 -0
  66. package/dist/prisma.cjs.map +1 -0
  67. package/dist/prisma.d.cts +31 -0
  68. package/dist/prisma.d.ts +31 -0
  69. package/dist/prisma.js +38 -0
  70. package/dist/prisma.js.map +1 -0
  71. package/package.json +138 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,677 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ DEFAULT_SKIP_PATHS: () => DEFAULT_SKIP_PATHS,
24
+ LEVELS: () => LEVELS,
25
+ REDACT_PATTERNS: () => REDACT_PATTERNS,
26
+ addContext: () => addContext,
27
+ createFetch: () => createFetch,
28
+ createLogger: () => createLogger,
29
+ getContext: () => getContext,
30
+ installProcessHandlers: () => installProcessHandlers,
31
+ jobData: () => jobData,
32
+ logHttpRequest: () => logHttpRequest,
33
+ logShutdown: () => logShutdown,
34
+ logStartup: () => logStartup,
35
+ parseTraceparent: () => parseTraceparent,
36
+ patchConsole: () => patchConsole,
37
+ propagationHeaders: () => propagationHeaders,
38
+ recordError: () => recordError,
39
+ requestContextFields: () => requestContextFields,
40
+ resolveRequestId: () => resolveRequestId,
41
+ runJob: () => runJob,
42
+ serializeError: () => serializeError,
43
+ withContext: () => withContext
44
+ });
45
+ module.exports = __toCommonJS(src_exports);
46
+
47
+ // src/context.ts
48
+ var import_node_async_hooks = require("async_hooks");
49
+
50
+ // src/global.ts
51
+ function globalSingleton(name, make) {
52
+ const key = /* @__PURE__ */ Symbol.for(`@tanngo11/log.${name}`);
53
+ const g = globalThis;
54
+ let value = g[key];
55
+ if (value === void 0) {
56
+ value = make();
57
+ g[key] = value;
58
+ }
59
+ return value;
60
+ }
61
+
62
+ // src/context.ts
63
+ var als = globalSingleton("als", () => new import_node_async_hooks.AsyncLocalStorage());
64
+ function runWithStore(fields, fn) {
65
+ const parent = als.getStore();
66
+ const store = { fields: { ...parent?.fields, ...fields }, parent };
67
+ return als.run(store, () => fn(store));
68
+ }
69
+ function withContext(fields, fn) {
70
+ return runWithStore(fields, () => fn());
71
+ }
72
+ function getContext() {
73
+ return als.getStore()?.fields ?? {};
74
+ }
75
+ function addContext(fields) {
76
+ for (let s = als.getStore(); s; s = s.parent) Object.assign(s.fields, fields);
77
+ }
78
+ function recordErrorIn(store, err) {
79
+ store.error = err;
80
+ for (let s = store.parent; s; s = s.parent) if (s.error === void 0) s.error = err;
81
+ }
82
+ function recordError(err) {
83
+ const store = als.getStore();
84
+ if (store) recordErrorIn(store, err);
85
+ }
86
+
87
+ // src/levels.ts
88
+ var LEVELS = ["trace", "debug", "info", "warn", "error", "fatal"];
89
+ var LEVEL_RANK = {
90
+ trace: 10,
91
+ debug: 20,
92
+ info: 30,
93
+ warn: 40,
94
+ error: 50,
95
+ fatal: 60
96
+ };
97
+ var ALIASES = { warning: "warn", err: "error", critical: "fatal", panic: "fatal" };
98
+ function parseLevel(value, fallback = "info") {
99
+ if (typeof value !== "string") return fallback;
100
+ const v = value.trim().toLowerCase();
101
+ if (LEVELS.includes(v)) return v;
102
+ return ALIASES[v] ?? fallback;
103
+ }
104
+
105
+ // src/redact.ts
106
+ var DEFAULT_REDACT_KEYS = [
107
+ "password",
108
+ "passwd",
109
+ "pwd",
110
+ "passphrase",
111
+ "secret",
112
+ "client_secret",
113
+ "token",
114
+ "access_token",
115
+ "refresh_token",
116
+ "id_token",
117
+ "auth_token",
118
+ "api_key",
119
+ "apikey",
120
+ "access_key",
121
+ "secret_key",
122
+ "private_key",
123
+ "authorization",
124
+ "cookie",
125
+ "set_cookie",
126
+ "session_id",
127
+ "sessionid",
128
+ "credential",
129
+ "credentials",
130
+ "dsn",
131
+ "connection_string",
132
+ "conn_string",
133
+ "jwt",
134
+ "bearer"
135
+ ];
136
+ var REDACT_PATTERNS = {
137
+ /** Vietnamese mobile numbers: 0912345678, +84912345678, 84912345678. */
138
+ phoneVN: /(?<![\d+])(?:\+?84|0)[35789]\d{8}(?!\d)/g,
139
+ /** Visa, Mastercard, Amex, Discover-like card numbers, with optional spaces or dashes. */
140
+ paymentCard: /(?<!\d)(?:4\d{3}|5[1-5]\d{2}|2[2-7]\d{2}|3[47]\d{2}|6(?:011|5\d{2}))(?:[ -]?\d{4}){2}[ -]?\d{1,4}(?!\d)/g,
141
+ /** IPv4 addresses. */
142
+ ipv4: /(?<![\d.])(?:\d{1,3}\.){3}\d{1,3}(?![\d.])/g
143
+ };
144
+ var SAFE_SUFFIXES = ["_count", "_type", "_length", "_expires_at", "_expires_in", "_ttl", "_at", "_ms", "_enabled"];
145
+ var REDACTED = "[REDACTED]";
146
+ function toSnakeCase(key) {
147
+ return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/[^A-Za-z0-9_]+/g, "_").toLowerCase();
148
+ }
149
+ var URL_CREDENTIALS_RE = /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:"']+:[^\s/@"']+@/gi;
150
+ var BEARER_RE = /\bBearer\s+[A-Za-z0-9._~+/=-]+/gi;
151
+ var JWT_RE = /\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g;
152
+ var EMAIL_RE = /[A-Za-z0-9._%+-]+@([A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,})/g;
153
+ function globalize(re) {
154
+ return re.flags.includes("g") ? new RegExp(re.source, re.flags) : new RegExp(re.source, `${re.flags}g`);
155
+ }
156
+ function createRedactor(opts = {}) {
157
+ const words = [...DEFAULT_REDACT_KEYS];
158
+ const keyPatterns = [];
159
+ for (const k of opts.keys ?? []) {
160
+ if (typeof k === "string") words.push(toSnakeCase(k));
161
+ else keyPatterns.push(new RegExp(k.source, k.flags.replace("g", "")));
162
+ }
163
+ const valuePatterns = (opts.values ?? []).map(globalize);
164
+ const isSensitive = (key) => {
165
+ const k = toSnakeCase(key);
166
+ if (keyPatterns.some((re) => re.test(k))) return true;
167
+ if (!words.includes(k) && SAFE_SUFFIXES.some((s) => k.endsWith(s))) return false;
168
+ return words.some((w) => k === w || k.startsWith(`${w}_`) || k.endsWith(`_${w}`) || k.includes(`_${w}_`));
169
+ };
170
+ const scrub = (s) => {
171
+ let out = s.replace(URL_CREDENTIALS_RE, `$1${REDACTED}@`).replace(BEARER_RE, `Bearer ${REDACTED}`).replace(JWT_RE, "[REDACTED_JWT]").replace(EMAIL_RE, "***@$1");
172
+ for (const re of valuePatterns) out = out.replace(re, REDACTED);
173
+ return out;
174
+ };
175
+ const json = (value) => {
176
+ if (typeof value === "string") return value;
177
+ try {
178
+ const text = JSON.stringify(value, (key, val) => {
179
+ if (key && val !== null && val !== void 0 && isSensitive(key)) return REDACTED;
180
+ if (typeof val === "bigint") return val.toString();
181
+ if (val instanceof Error) return { name: val.name, message: val.message };
182
+ return val;
183
+ });
184
+ return text ?? String(value);
185
+ } catch {
186
+ return String(value);
187
+ }
188
+ };
189
+ return { isSensitive, scrub, json };
190
+ }
191
+ var defaultRedactor = createRedactor();
192
+
193
+ // src/error.ts
194
+ var MAX_CAUSE_DEPTH = 5;
195
+ function isErrorLike(v) {
196
+ if (v instanceof Error) return true;
197
+ return typeof v === "object" && v !== null && typeof v.message === "string" && ("stack" in v || "name" in v);
198
+ }
199
+ function errorType(e) {
200
+ if (e.name && e.name !== "Error") return e.name;
201
+ const ctor = e.constructor?.name;
202
+ return ctor && ctor !== "Object" ? ctor : "Error";
203
+ }
204
+ function serializeError(err) {
205
+ if (!isErrorLike(err)) {
206
+ if (typeof err === "string") return { err_type: "string", err_message: err };
207
+ return { err_type: typeof err, err_message: defaultRedactor.json(err) };
208
+ }
209
+ const e = err;
210
+ const out = { err_type: errorType(e), err_message: String(e.message ?? "") };
211
+ if (e.code !== void 0 && e.code !== null) out.err_code = String(e.code);
212
+ if (typeof e.stack === "string") out.err_stack = e.stack;
213
+ const causes = [];
214
+ let cause = e.cause;
215
+ while (cause !== void 0 && cause !== null && causes.length < MAX_CAUSE_DEPTH) {
216
+ if (isErrorLike(cause)) {
217
+ causes.push(`${errorType(cause)}: ${cause.message}`);
218
+ cause = cause.cause;
219
+ } else {
220
+ causes.push(defaultRedactor.json(cause));
221
+ cause = void 0;
222
+ }
223
+ }
224
+ if (causes.length > 0) out.err_cause = causes.join(" <- ");
225
+ return out;
226
+ }
227
+ var logged = globalSingleton("logged", () => /* @__PURE__ */ new WeakSet());
228
+ function markLogged(err) {
229
+ if (typeof err === "object" && err !== null) logged.add(err);
230
+ }
231
+
232
+ // src/normalize.ts
233
+ var HOOK_EXEMPT = /* @__PURE__ */ new Set(["level", "message", "event"]);
234
+ var RESERVED_KEYS = /* @__PURE__ */ new Set([
235
+ "project",
236
+ "service",
237
+ "vps",
238
+ "version",
239
+ "container_name",
240
+ "container_id",
241
+ "image",
242
+ "stream",
243
+ "_timestamp",
244
+ "label",
245
+ "host",
246
+ "source_type",
247
+ "msg",
248
+ "lvl",
249
+ "severity",
250
+ "level",
251
+ "message"
252
+ ]);
253
+ var MAX_LINE_BYTES = 16e3;
254
+ var MAX_MESSAGE = 500;
255
+ var MAX_STACK = 4e3;
256
+ var MAX_STRING = 1e3;
257
+ var MAX_DEPTH = 3;
258
+ var TRUNCATED = "\u2026[truncated]";
259
+ var KEEP_ON_SHRINK = /* @__PURE__ */ new Set([
260
+ "level",
261
+ "message",
262
+ "event",
263
+ "request_id",
264
+ "trace_id",
265
+ "span_id",
266
+ "job_name",
267
+ "job_id",
268
+ "job_attempt",
269
+ "user_id",
270
+ "tenant_id",
271
+ "err_type",
272
+ "err_message",
273
+ "err_code"
274
+ ]);
275
+ function stripUrl(url) {
276
+ const noQuery = url.split(/[?#]/, 1)[0] ?? "";
277
+ return noQuery.replace(/^([a-z][a-z0-9+.-]*:\/\/)[^/@\s]*@/i, "$1");
278
+ }
279
+ function truncate(s, max) {
280
+ return s.length > max ? s.slice(0, max - TRUNCATED.length) + TRUNCATED : s;
281
+ }
282
+ function isPlainObject(v) {
283
+ if (typeof v !== "object" || v === null) return false;
284
+ const proto = Object.getPrototypeOf(v);
285
+ return proto === Object.prototype || proto === null;
286
+ }
287
+ function isUrlKey(key) {
288
+ return key === "http_path" || key === "http_url" || key.endsWith("_url");
289
+ }
290
+ function flattenInto(out, prefix, obj, depth, opts) {
291
+ for (const [rawKey, value] of Object.entries(obj)) {
292
+ const key = opts.snakeCase ? toSnakeCase(rawKey) : rawKey;
293
+ const full = prefix ? `${prefix}_${key}` : key;
294
+ if (value === void 0 || typeof value === "function" || typeof value === "symbol") continue;
295
+ if (value !== null && (opts.redactor.isSensitive(rawKey) || opts.redactor.isSensitive(full))) {
296
+ out[full] = REDACTED;
297
+ continue;
298
+ }
299
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
300
+ out[full] = value;
301
+ } else if (typeof value === "number") {
302
+ out[full] = Number.isFinite(value) ? value : String(value);
303
+ } else if (typeof value === "bigint") {
304
+ out[full] = value.toString();
305
+ } else if (value instanceof Date) {
306
+ out[full] = Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
307
+ } else if (isErrorLike(value)) {
308
+ out[full] = `${value.name}: ${value.message}`;
309
+ } else if (isPlainObject(value) && depth < MAX_DEPTH - 1) {
310
+ flattenInto(out, full, value, depth + 1, opts);
311
+ } else {
312
+ out[full] = opts.redactor.json(value);
313
+ }
314
+ }
315
+ }
316
+ function finalize(key, value, opts) {
317
+ if (typeof value !== "string") return value;
318
+ const v = opts.redactor.scrub(isUrlKey(key) ? stripUrl(value) : value);
319
+ return truncate(v, key === "err_stack" ? MAX_STACK : MAX_STRING);
320
+ }
321
+ function buildRecord(level, message, sources, opts) {
322
+ const merged = {};
323
+ for (const source of sources) if (source) Object.assign(merged, source);
324
+ let err;
325
+ if (merged.err !== void 0 && merged.err !== null) {
326
+ err = merged.err;
327
+ delete merged.err;
328
+ } else if (isErrorLike(merged.error)) {
329
+ err = merged.error;
330
+ delete merged.error;
331
+ }
332
+ const flat = {};
333
+ flattenInto(flat, "", merged, 0, opts);
334
+ if (err !== void 0) Object.assign(flat, serializeError(err));
335
+ const text = typeof message === "string" ? message : opts.redactor.json(message);
336
+ const out = { level, message: truncate(opts.redactor.scrub(text), MAX_MESSAGE) };
337
+ for (const [k, v] of Object.entries(flat)) {
338
+ const key = RESERVED_KEYS.has(k) ? `app_${k}` : k;
339
+ let value = finalize(key, v, opts);
340
+ if (opts.hook && !HOOK_EXEMPT.has(key)) {
341
+ const replaced = opts.hook(key, value);
342
+ if (replaced === void 0) continue;
343
+ value = typeof replaced === "object" && replaced !== null ? opts.redactor.json(replaced) : replaced;
344
+ }
345
+ out[key] = value;
346
+ }
347
+ return out;
348
+ }
349
+ function byteLength(s) {
350
+ if (s.length * 3 <= MAX_LINE_BYTES) return s.length;
351
+ return new TextEncoder().encode(s).length;
352
+ }
353
+ function serializeRecord(record) {
354
+ const line = JSON.stringify(record);
355
+ if (byteLength(line) <= MAX_LINE_BYTES) return line;
356
+ const small = {};
357
+ for (const [k, v] of Object.entries(record)) {
358
+ if (KEEP_ON_SHRINK.has(k) || k.startsWith("http_")) small[k] = v;
359
+ }
360
+ small.log_truncated = true;
361
+ return JSON.stringify(small);
362
+ }
363
+
364
+ // src/pretty.ts
365
+ function formatPretty(record) {
366
+ const { level, message, err_stack, ...rest } = record;
367
+ const time = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
368
+ const extras = Object.entries(rest).map(([k, v]) => `${k}=${typeof v === "string" && /\s/.test(v) ? JSON.stringify(v) : String(v)}`).join(" ");
369
+ let line = `${time} ${String(level).toUpperCase().padEnd(5)} ${String(message)}${extras ? ` ${extras}` : ""}`;
370
+ if (typeof err_stack === "string") line += `
371
+ ${err_stack}`;
372
+ return line;
373
+ }
374
+
375
+ // src/logger.ts
376
+ function env(name) {
377
+ return typeof process !== "undefined" ? process.env?.[name] : void 0;
378
+ }
379
+ var consoleLog = console.log.bind(console);
380
+ function defaultWrite(line) {
381
+ if (typeof process !== "undefined" && typeof process.stdout?.write === "function") {
382
+ process.stdout.write(`${line}
383
+ `);
384
+ } else {
385
+ consoleLog(line);
386
+ }
387
+ }
388
+ function createLogger(options = {}) {
389
+ const level = parseLevel(options.level ?? env("LOG_LEVEL"));
390
+ const format = options.format ?? (env("LOG_FORMAT") === "pretty" ? "pretty" : "json");
391
+ const write = options.write ?? defaultWrite;
392
+ const norm = {
393
+ snakeCase: options.snakeCase ?? true,
394
+ redactor: createRedactor({ keys: options.redactKeys, values: options.redactValues }),
395
+ hook: options.redact
396
+ };
397
+ const make = (bound) => {
398
+ const emit = (lvl, message, fields) => {
399
+ if (LEVEL_RANK[lvl] < LEVEL_RANK[level]) return;
400
+ let line;
401
+ try {
402
+ const record = buildRecord(lvl, message, [options.base, getContext(), bound, fields], norm);
403
+ line = format === "pretty" ? formatPretty(record) : serializeRecord(record);
404
+ } catch (e) {
405
+ line = JSON.stringify({
406
+ level: "error",
407
+ message: "log serialize failed",
408
+ event: "log.serialize_failed",
409
+ err_message: String(e?.message ?? e).slice(0, 1e3),
410
+ original_message: String(message).slice(0, 500)
411
+ });
412
+ }
413
+ try {
414
+ write(line);
415
+ } catch {
416
+ }
417
+ };
418
+ return {
419
+ level,
420
+ trace: (m, f) => emit("trace", m, f),
421
+ debug: (m, f) => emit("debug", m, f),
422
+ info: (m, f) => emit("info", m, f),
423
+ warn: (m, f) => emit("warn", m, f),
424
+ error: (m, f) => emit("error", m, f),
425
+ fatal: (m, f) => emit("fatal", m, f),
426
+ child: (fields) => make({ ...bound, ...fields }),
427
+ isLevelEnabled: (l) => LEVEL_RANK[l] >= LEVEL_RANK[level]
428
+ };
429
+ };
430
+ return make({});
431
+ }
432
+
433
+ // src/request-id.ts
434
+ var REQUEST_ID_RE = /^[A-Za-z0-9._-]{1,64}$/;
435
+ var TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})$/;
436
+ function newRequestId() {
437
+ return globalThis.crypto.randomUUID();
438
+ }
439
+ function first(header2) {
440
+ return Array.isArray(header2) ? header2[0] : header2 ?? void 0;
441
+ }
442
+ function resolveRequestId(header2) {
443
+ const value = first(header2);
444
+ return value && REQUEST_ID_RE.test(value) ? value : newRequestId();
445
+ }
446
+ function parseTraceparent(header2) {
447
+ const value = first(header2)?.trim().toLowerCase();
448
+ const m = value ? TRACEPARENT_RE.exec(value) : null;
449
+ if (!m) return void 0;
450
+ const [, version, traceId, spanId] = m;
451
+ if (version === "ff" || /^0+$/.test(traceId) || /^0+$/.test(spanId)) return void 0;
452
+ return { trace_id: traceId, span_id: spanId };
453
+ }
454
+
455
+ // src/http.ts
456
+ function header(headers, name) {
457
+ if (typeof headers.get === "function") return headers.get(name) ?? void 0;
458
+ return headers[name];
459
+ }
460
+ function requestContextFields(headers) {
461
+ const out = {
462
+ request_id: resolveRequestId(header(headers, "x-request-id"))
463
+ };
464
+ const tp = parseTraceparent(header(headers, "traceparent"));
465
+ if (tp) Object.assign(out, tp);
466
+ return out;
467
+ }
468
+ function propagationHeaders() {
469
+ const ctx = getContext();
470
+ const out = {};
471
+ if (typeof ctx.request_id === "string") out["x-request-id"] = ctx.request_id;
472
+ if (typeof ctx.trace_id === "string" && typeof ctx.span_id === "string") {
473
+ out.traceparent = `00-${ctx.trace_id}-${ctx.span_id}-01`;
474
+ }
475
+ return out;
476
+ }
477
+ var DEFAULT_SKIP_PATHS = [
478
+ /^\/(health|healthz|ready|readyz|livez)\/?$/,
479
+ "/favicon.ico",
480
+ /^\/_next\/static\//
481
+ ];
482
+ function shouldSkip(method, path, skipPaths = DEFAULT_SKIP_PATHS) {
483
+ if (method.toUpperCase() === "OPTIONS") return true;
484
+ const p = stripUrl(path);
485
+ return skipPaths.some((s) => typeof s === "string" ? p === s : s.test(p));
486
+ }
487
+ function logHttpRequest(log, info, opts = {}) {
488
+ if (shouldSkip(info.method, info.path, opts.skipPaths)) return;
489
+ const level = info.status >= 500 ? "error" : info.status >= 400 ? "warn" : "info";
490
+ const path = stripUrl(info.path);
491
+ const errFields = {};
492
+ if (info.err !== void 0 && info.err !== null) {
493
+ Object.assign(errFields, serializeError(info.err));
494
+ if (info.status < 500) delete errFields.err_stack;
495
+ markLogged(info.err);
496
+ }
497
+ const message = info.route ? `${info.method} ${info.route} ${info.status}` : `${info.method} ${info.status}`;
498
+ log[level](message, {
499
+ ...info.fields,
500
+ event: "http.request",
501
+ http_method: info.method,
502
+ http_route: info.route,
503
+ http_path: path,
504
+ http_status: info.status,
505
+ duration_ms: info.duration_ms === void 0 ? void 0 : Math.round(info.duration_ms),
506
+ ...errFields
507
+ });
508
+ }
509
+
510
+ // src/fetch.ts
511
+ function describeRequest(input, init) {
512
+ const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
513
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
514
+ return { method, url };
515
+ }
516
+ function createFetch(log, opts) {
517
+ const slowMs = opts.slowMs ?? 1e3;
518
+ const wrapped = async (input, init) => {
519
+ const base = opts.fetch ?? globalThis.fetch;
520
+ const { method, url } = describeRequest(input, init);
521
+ let requestInit = init;
522
+ if (opts.internal) {
523
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
524
+ for (const [k, v] of Object.entries(propagationHeaders())) if (!headers.has(k)) headers.set(k, v);
525
+ requestInit = { ...init, headers };
526
+ }
527
+ const fields = { event: "http.outbound", peer_service: opts.peer_service, http_method: method, http_url: url };
528
+ const start = performance.now();
529
+ try {
530
+ const res = await base(input, requestInit);
531
+ const duration_ms = Math.round(performance.now() - start);
532
+ if (res.status >= 500) {
533
+ log.warn("outbound request failed", { ...fields, http_status: res.status, duration_ms });
534
+ } else if (duration_ms >= slowMs) {
535
+ log.warn("outbound request slow", { ...fields, http_status: res.status, duration_ms });
536
+ }
537
+ return res;
538
+ } catch (err) {
539
+ log.warn("outbound request failed", { ...fields, duration_ms: Math.round(performance.now() - start), err });
540
+ throw err;
541
+ }
542
+ };
543
+ return wrapped;
544
+ }
545
+
546
+ // src/job.ts
547
+ async function runJob(log, info, fn) {
548
+ const ctx = { ...info.fields, job_name: info.job_name };
549
+ if (info.job_id !== void 0) ctx.job_id = String(info.job_id);
550
+ if (info.job_attempt !== void 0) ctx.job_attempt = info.job_attempt;
551
+ if (info.request_id !== void 0) ctx.request_id = info.request_id;
552
+ return withContext(ctx, async () => {
553
+ const start = performance.now();
554
+ try {
555
+ const result = await fn();
556
+ log.info("job completed", { event: "job.completed", duration_ms: Math.round(performance.now() - start) });
557
+ return result;
558
+ } catch (err) {
559
+ const final = typeof info.final_attempt === "function" ? info.final_attempt(err) : info.final_attempt ?? true;
560
+ log[final ? "error" : "warn"]("job failed", {
561
+ event: "job.failed",
562
+ duration_ms: Math.round(performance.now() - start),
563
+ err
564
+ });
565
+ markLogged(err);
566
+ throw err;
567
+ }
568
+ });
569
+ }
570
+ function jobData(data) {
571
+ const requestId = getContext().request_id;
572
+ if (typeof requestId !== "string" || data.request_id !== void 0) return data;
573
+ return { ...data, request_id: requestId };
574
+ }
575
+
576
+ // src/console.ts
577
+ var METHODS = { log: "info", info: "info", warn: "warn", error: "error", debug: "debug", trace: "debug" };
578
+ function patchConsole(log) {
579
+ const target = console;
580
+ const saved = {};
581
+ let inside = false;
582
+ for (const method of Object.keys(METHODS)) {
583
+ const previous = target[method];
584
+ saved[method] = previous;
585
+ const level = METHODS[method];
586
+ target[method] = (...args) => {
587
+ if (inside) return previous.apply(console, args);
588
+ inside = true;
589
+ try {
590
+ let err;
591
+ const parts = [];
592
+ for (const arg of args) {
593
+ if (isErrorLike(arg)) {
594
+ err ??= arg;
595
+ parts.push(arg.message);
596
+ } else {
597
+ parts.push(typeof arg === "string" ? arg : defaultRedactor.json(arg));
598
+ }
599
+ }
600
+ log[level](parts.join(" "), { event: "console", console_method: method, err });
601
+ } finally {
602
+ inside = false;
603
+ }
604
+ };
605
+ }
606
+ return () => {
607
+ for (const method of Object.keys(saved)) target[method] = saved[method];
608
+ };
609
+ }
610
+
611
+ // src/process.ts
612
+ function logStartup(log, fields = {}) {
613
+ log.info("app started", {
614
+ event: "app.started",
615
+ runtime_version: `node ${process.versions.node}`,
616
+ log_level: log.level,
617
+ ...fields
618
+ });
619
+ }
620
+ function logShutdown(log, fields = {}) {
621
+ log.info("app stopping", { event: "app.stopping", ...fields });
622
+ }
623
+ function crashHandlers(log, opts = {}) {
624
+ const exitOnCrash = opts.exitOnCrash ?? true;
625
+ const exitDelayMs = opts.exitDelayMs ?? 1e3;
626
+ const exit = opts.exit ?? ((code) => process.exit(code));
627
+ let exiting = false;
628
+ const crash = (source, err) => {
629
+ if (!exitOnCrash) {
630
+ log.error("unhandled error", { event: "app.unhandled_error", crash_source: source, err });
631
+ return;
632
+ }
633
+ log.fatal("app crashed", { event: "app.crashed", crash_source: source, err });
634
+ if (exiting) return;
635
+ exiting = true;
636
+ if (typeof process !== "undefined") process.exitCode = 1;
637
+ const timer = setTimeout(() => exit(1), exitDelayMs);
638
+ timer.unref?.();
639
+ };
640
+ return {
641
+ onUncaught: (err) => crash("uncaughtException", err),
642
+ onRejection: (reason) => crash("unhandledRejection", reason)
643
+ };
644
+ }
645
+ var state = globalSingleton("process", () => ({ installed: false }));
646
+ function installProcessHandlers(log, opts = {}) {
647
+ if (state.installed || typeof process === "undefined" || typeof process.on !== "function") return;
648
+ state.installed = true;
649
+ const h = crashHandlers(log, opts);
650
+ process.on("uncaughtException", h.onUncaught);
651
+ process.on("unhandledRejection", h.onRejection);
652
+ }
653
+ // Annotate the CommonJS export names for ESM import in node:
654
+ 0 && (module.exports = {
655
+ DEFAULT_SKIP_PATHS,
656
+ LEVELS,
657
+ REDACT_PATTERNS,
658
+ addContext,
659
+ createFetch,
660
+ createLogger,
661
+ getContext,
662
+ installProcessHandlers,
663
+ jobData,
664
+ logHttpRequest,
665
+ logShutdown,
666
+ logStartup,
667
+ parseTraceparent,
668
+ patchConsole,
669
+ propagationHeaders,
670
+ recordError,
671
+ requestContextFields,
672
+ resolveRequestId,
673
+ runJob,
674
+ serializeError,
675
+ withContext
676
+ });
677
+ //# sourceMappingURL=index.cjs.map