loggily 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/context.d.mts +91 -0
- package/dist/context.d.mts.map +1 -0
- package/dist/context.mjs +145 -0
- package/dist/context.mjs.map +1 -0
- package/dist/core-7D7sstHl.d.mts +239 -0
- package/dist/core-7D7sstHl.d.mts.map +1 -0
- package/dist/core-BDFU50FQ.mjs +570 -0
- package/dist/core-BDFU50FQ.mjs.map +1 -0
- package/dist/file-writer-BuQGFGRs.d.mts +46 -0
- package/dist/file-writer-BuQGFGRs.d.mts.map +1 -0
- package/dist/file-writer.d.mts +2 -0
- package/dist/file-writer.mjs +75 -0
- package/dist/file-writer.mjs.map +1 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/metrics.d.mts +48 -0
- package/dist/metrics.d.mts.map +1 -0
- package/dist/metrics.mjs +130 -0
- package/dist/metrics.mjs.map +1 -0
- package/dist/tracing-2kv3HZ07.d.mts +65 -0
- package/dist/tracing-2kv3HZ07.d.mts.map +1 -0
- package/dist/tracing.d.mts +2 -0
- package/dist/tracing.mjs +96 -0
- package/dist/tracing.mjs.map +1 -0
- package/dist/worker.d.mts +173 -0
- package/dist/worker.d.mts.map +1 -0
- package/dist/worker.mjs +468 -0
- package/dist/worker.mjs.map +1 -0
- package/package.json +18 -15
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import { generateSpanId, generateTraceId, resetIdCounters, shouldSample } from "./tracing.mjs";
|
|
2
|
+
//#region src/colors.ts
|
|
3
|
+
/**
|
|
4
|
+
* Vendored ANSI color functions — replaces picocolors dependency.
|
|
5
|
+
* Supports NO_COLOR, FORCE_COLOR, and TTY detection.
|
|
6
|
+
*/
|
|
7
|
+
const _process$1 = typeof process !== "undefined" ? process : void 0;
|
|
8
|
+
const enabled = _process$1?.env?.["FORCE_COLOR"] !== void 0 && _process$1?.env?.["FORCE_COLOR"] !== "0" ? true : _process$1?.env?.["NO_COLOR"] !== void 0 ? false : _process$1?.stdout?.isTTY ?? false;
|
|
9
|
+
function wrap(open, close) {
|
|
10
|
+
if (!enabled) return (str) => str;
|
|
11
|
+
return (str) => open + str + close;
|
|
12
|
+
}
|
|
13
|
+
const colors = {
|
|
14
|
+
dim: wrap("\x1B[2m", "\x1B[22m"),
|
|
15
|
+
blue: wrap("\x1B[34m", "\x1B[39m"),
|
|
16
|
+
yellow: wrap("\x1B[33m", "\x1B[39m"),
|
|
17
|
+
red: wrap("\x1B[31m", "\x1B[39m"),
|
|
18
|
+
magenta: wrap("\x1B[35m", "\x1B[39m"),
|
|
19
|
+
cyan: wrap("\x1B[36m", "\x1B[39m")
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/core.ts
|
|
23
|
+
/**
|
|
24
|
+
* loggily - Structured logging with spans
|
|
25
|
+
*
|
|
26
|
+
* Logger-first architecture: Span = Logger + Duration
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const log = createLogger('myapp')
|
|
30
|
+
*
|
|
31
|
+
* // Simple logging
|
|
32
|
+
* log.info('starting')
|
|
33
|
+
*
|
|
34
|
+
* // Lazy messages (function not called when level is disabled)
|
|
35
|
+
* log.debug?.(() => `expensive: ${computeState()}`)
|
|
36
|
+
*
|
|
37
|
+
* // Child loggers with context fields
|
|
38
|
+
* const reqLog = log.child({ requestId: 'abc' })
|
|
39
|
+
* reqLog.info('handling request') // includes requestId in every message
|
|
40
|
+
*
|
|
41
|
+
* // With timing (span)
|
|
42
|
+
* {
|
|
43
|
+
* using task = log.span('import', { file: 'data.csv' })
|
|
44
|
+
* task.info('importing')
|
|
45
|
+
* task.spanData.count = 42 // Set span attributes
|
|
46
|
+
* // Auto-disposal on block exit → SPAN myapp:import (15ms)
|
|
47
|
+
* }
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Ambient span recorder — auto-records when TRACE is active.
|
|
51
|
+
* Set by metrics.ts on import; can be replaced for testing.
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
let _ambientRecorder = null;
|
|
55
|
+
function _setAmbientRecorder(recorder) {
|
|
56
|
+
_ambientRecorder = recorder;
|
|
57
|
+
}
|
|
58
|
+
/** Cached process reference — undefined in browser/edge runtimes */
|
|
59
|
+
const _process = typeof process !== "undefined" ? process : void 0;
|
|
60
|
+
/** Read an environment variable, returning undefined in non-Node runtimes */
|
|
61
|
+
function getEnv(key) {
|
|
62
|
+
return _process?.env?.[key];
|
|
63
|
+
}
|
|
64
|
+
/** Write to stderr with console.error fallback for non-Node runtimes */
|
|
65
|
+
function writeStderr(text) {
|
|
66
|
+
if (_process?.stderr?.write) _process.stderr.write(text + "\n");
|
|
67
|
+
else console.error(text);
|
|
68
|
+
}
|
|
69
|
+
const writers = [];
|
|
70
|
+
/** Add a writer that receives all formatted log output. Returns unsubscribe. */
|
|
71
|
+
function addWriter(writer) {
|
|
72
|
+
writers.push(writer);
|
|
73
|
+
return () => {
|
|
74
|
+
const idx = writers.indexOf(writer);
|
|
75
|
+
if (idx !== -1) writers.splice(idx, 1);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
let suppressConsole = false;
|
|
79
|
+
/** Suppress console output from the logger (writers still receive output). */
|
|
80
|
+
function setSuppressConsole(value) {
|
|
81
|
+
suppressConsole = value;
|
|
82
|
+
}
|
|
83
|
+
let outputMode = "console";
|
|
84
|
+
/** Set output mode for log messages (not spans — spans always use stderr). */
|
|
85
|
+
function setOutputMode(mode) {
|
|
86
|
+
outputMode = mode;
|
|
87
|
+
}
|
|
88
|
+
/** Get current output mode */
|
|
89
|
+
function getOutputMode() {
|
|
90
|
+
return outputMode;
|
|
91
|
+
}
|
|
92
|
+
const LOG_LEVEL_PRIORITY = {
|
|
93
|
+
trace: 0,
|
|
94
|
+
debug: 1,
|
|
95
|
+
info: 2,
|
|
96
|
+
warn: 3,
|
|
97
|
+
error: 4,
|
|
98
|
+
silent: 5
|
|
99
|
+
};
|
|
100
|
+
const envLogLevel = getEnv("LOG_LEVEL")?.toLowerCase();
|
|
101
|
+
let currentLogLevel = envLogLevel === "trace" || envLogLevel === "debug" || envLogLevel === "info" || envLogLevel === "warn" || envLogLevel === "error" || envLogLevel === "silent" ? envLogLevel : "info";
|
|
102
|
+
const traceEnv = getEnv("TRACE");
|
|
103
|
+
let spansEnabled = traceEnv === "1" || traceEnv === "true";
|
|
104
|
+
let traceFilter = null;
|
|
105
|
+
if (traceEnv && traceEnv !== "1" && traceEnv !== "true") {
|
|
106
|
+
traceFilter = new Set(traceEnv.split(",").map((s) => s.trim()));
|
|
107
|
+
spansEnabled = true;
|
|
108
|
+
}
|
|
109
|
+
/** Parse a comma-separated namespace filter into include/exclude sets */
|
|
110
|
+
function parseNamespaceFilter(input) {
|
|
111
|
+
const includeList = [];
|
|
112
|
+
const excludeList = [];
|
|
113
|
+
for (const part of input) if (part.startsWith("-")) excludeList.push(part.slice(1));
|
|
114
|
+
else includeList.push(part);
|
|
115
|
+
return {
|
|
116
|
+
includes: includeList.length > 0 ? new Set(includeList) : null,
|
|
117
|
+
excludes: excludeList.length > 0 ? new Set(excludeList) : null
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const debugEnv = getEnv("DEBUG");
|
|
121
|
+
let debugIncludes = null;
|
|
122
|
+
let debugExcludes = null;
|
|
123
|
+
if (debugEnv) {
|
|
124
|
+
const parsed = parseNamespaceFilter(debugEnv.split(",").map((s) => s.trim()));
|
|
125
|
+
debugIncludes = parsed.includes;
|
|
126
|
+
if (debugIncludes && [...debugIncludes].some((p) => p === "*" || p === "1" || p === "true")) debugIncludes = new Set(["*"]);
|
|
127
|
+
debugExcludes = parsed.excludes;
|
|
128
|
+
if (LOG_LEVEL_PRIORITY[currentLogLevel] > LOG_LEVEL_PRIORITY.debug) currentLogLevel = "debug";
|
|
129
|
+
}
|
|
130
|
+
/** Set minimum log level */
|
|
131
|
+
function setLogLevel(level) {
|
|
132
|
+
currentLogLevel = level;
|
|
133
|
+
}
|
|
134
|
+
/** Get current log level */
|
|
135
|
+
function getLogLevel() {
|
|
136
|
+
return currentLogLevel;
|
|
137
|
+
}
|
|
138
|
+
/** Enable span output */
|
|
139
|
+
function enableSpans() {
|
|
140
|
+
spansEnabled = true;
|
|
141
|
+
}
|
|
142
|
+
/** Disable span output */
|
|
143
|
+
function disableSpans() {
|
|
144
|
+
spansEnabled = false;
|
|
145
|
+
}
|
|
146
|
+
/** Check if spans are enabled */
|
|
147
|
+
function spansAreEnabled() {
|
|
148
|
+
return spansEnabled;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Set trace filter for namespace-based span output control.
|
|
152
|
+
* Only spans matching these namespace prefixes will be output.
|
|
153
|
+
* @param namespaces - Array of namespace prefixes, or null to disable filtering
|
|
154
|
+
*/
|
|
155
|
+
function setTraceFilter(namespaces) {
|
|
156
|
+
if (namespaces === null || namespaces.length === 0) traceFilter = null;
|
|
157
|
+
else {
|
|
158
|
+
traceFilter = new Set(namespaces);
|
|
159
|
+
spansEnabled = true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** Get current trace filter (null means no filtering) */
|
|
163
|
+
function getTraceFilter() {
|
|
164
|
+
return traceFilter ? [...traceFilter] : null;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Set debug namespace filter (like the `debug` npm package).
|
|
168
|
+
* When set, only loggers matching these namespace prefixes produce output.
|
|
169
|
+
* Supports negative patterns with `-` prefix (e.g., ["-km:noisy"]).
|
|
170
|
+
* Also ensures log level is at least `debug`.
|
|
171
|
+
* @param namespaces - Array of namespace prefixes (prefix with `-` to exclude), or null to disable
|
|
172
|
+
*/
|
|
173
|
+
function setDebugFilter(namespaces) {
|
|
174
|
+
if (namespaces === null || namespaces.length === 0) {
|
|
175
|
+
debugIncludes = null;
|
|
176
|
+
debugExcludes = null;
|
|
177
|
+
} else {
|
|
178
|
+
const parsed = parseNamespaceFilter(namespaces);
|
|
179
|
+
debugIncludes = parsed.includes;
|
|
180
|
+
debugExcludes = parsed.excludes;
|
|
181
|
+
if (LOG_LEVEL_PRIORITY[currentLogLevel] > LOG_LEVEL_PRIORITY.debug) currentLogLevel = "debug";
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Get current debug namespace filter (null means no filtering) */
|
|
185
|
+
function getDebugFilter() {
|
|
186
|
+
if (!debugIncludes && !debugExcludes) return null;
|
|
187
|
+
const result = [];
|
|
188
|
+
if (debugIncludes) result.push(...debugIncludes);
|
|
189
|
+
if (debugExcludes) result.push(...[...debugExcludes].map((e) => `-${e}`));
|
|
190
|
+
return result;
|
|
191
|
+
}
|
|
192
|
+
const envLogFormat = getEnv("LOG_FORMAT")?.toLowerCase();
|
|
193
|
+
let currentLogFormat = envLogFormat === "json" ? "json" : envLogFormat === "console" ? "console" : "console";
|
|
194
|
+
/** Set log output format */
|
|
195
|
+
function setLogFormat(format) {
|
|
196
|
+
currentLogFormat = format;
|
|
197
|
+
}
|
|
198
|
+
/** Get current log output format */
|
|
199
|
+
function getLogFormat() {
|
|
200
|
+
return currentLogFormat;
|
|
201
|
+
}
|
|
202
|
+
/** Determine whether to use JSON formatting for the current call */
|
|
203
|
+
function useJsonFormat() {
|
|
204
|
+
return currentLogFormat === "json" || getEnv("NODE_ENV") === "production" || getEnv("TRACE_FORMAT") === "json";
|
|
205
|
+
}
|
|
206
|
+
function resetIds() {
|
|
207
|
+
resetIdCounters();
|
|
208
|
+
}
|
|
209
|
+
/** Hook to get current span context tags (trace_id, span_id) for auto-tagging logs */
|
|
210
|
+
let _getContextTags = null;
|
|
211
|
+
/** Hook to get parent span info from async context */
|
|
212
|
+
let _getContextParent = null;
|
|
213
|
+
/** Hook to enter a span context (sets AsyncLocalStorage for the current async scope) */
|
|
214
|
+
let _enterContext = null;
|
|
215
|
+
/** Hook to exit a span context (restores previous context snapshot) */
|
|
216
|
+
let _exitContext = null;
|
|
217
|
+
/**
|
|
218
|
+
* Register context propagation hooks (called by context.ts).
|
|
219
|
+
* @internal
|
|
220
|
+
*/
|
|
221
|
+
function _setContextHooks(hooks) {
|
|
222
|
+
_getContextTags = hooks.getContextTags;
|
|
223
|
+
_getContextParent = hooks.getContextParent;
|
|
224
|
+
_enterContext = hooks.enterContext;
|
|
225
|
+
_exitContext = hooks.exitContext;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Clear context propagation hooks (called by disableContextPropagation).
|
|
229
|
+
* @internal
|
|
230
|
+
*/
|
|
231
|
+
function _clearContextHooks() {
|
|
232
|
+
_getContextTags = null;
|
|
233
|
+
_getContextParent = null;
|
|
234
|
+
_enterContext = null;
|
|
235
|
+
_exitContext = null;
|
|
236
|
+
}
|
|
237
|
+
function shouldLog(level) {
|
|
238
|
+
return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentLogLevel];
|
|
239
|
+
}
|
|
240
|
+
function shouldTraceNamespace(namespace) {
|
|
241
|
+
if (!spansEnabled) return false;
|
|
242
|
+
if (!traceFilter) return true;
|
|
243
|
+
return matchesNamespaceSet(namespace, traceFilter);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Safe JSON.stringify that handles bigint, circular refs, symbols, and Error objects.
|
|
247
|
+
* Prevents crashes from non-serializable values in log data.
|
|
248
|
+
*/
|
|
249
|
+
function safeStringify(value) {
|
|
250
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
251
|
+
return JSON.stringify(value, (_key, val) => {
|
|
252
|
+
if (typeof val === "bigint") return val.toString();
|
|
253
|
+
if (typeof val === "symbol") return val.toString();
|
|
254
|
+
if (val instanceof Error) return {
|
|
255
|
+
message: val.message,
|
|
256
|
+
stack: val.stack,
|
|
257
|
+
name: val.name
|
|
258
|
+
};
|
|
259
|
+
if (typeof val === "object" && val !== null) {
|
|
260
|
+
if (seen.has(val)) return "[Circular]";
|
|
261
|
+
seen.add(val);
|
|
262
|
+
}
|
|
263
|
+
return val;
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function formatConsole(namespace, level, message, data) {
|
|
267
|
+
const time = colors.dim((/* @__PURE__ */ new Date()).toISOString().split("T")[1]?.split(".")[0] || "");
|
|
268
|
+
let levelStr = "";
|
|
269
|
+
switch (level) {
|
|
270
|
+
case "trace":
|
|
271
|
+
levelStr = colors.dim("TRACE");
|
|
272
|
+
break;
|
|
273
|
+
case "debug":
|
|
274
|
+
levelStr = colors.dim("DEBUG");
|
|
275
|
+
break;
|
|
276
|
+
case "info":
|
|
277
|
+
levelStr = colors.blue("INFO");
|
|
278
|
+
break;
|
|
279
|
+
case "warn":
|
|
280
|
+
levelStr = colors.yellow("WARN");
|
|
281
|
+
break;
|
|
282
|
+
case "error":
|
|
283
|
+
levelStr = colors.red("ERROR");
|
|
284
|
+
break;
|
|
285
|
+
case "span":
|
|
286
|
+
levelStr = colors.magenta("SPAN");
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
const ns = colors.cyan(namespace);
|
|
290
|
+
let output = `${time} ${levelStr} ${ns} ${message}`;
|
|
291
|
+
if (data && Object.keys(data).length > 0) output += ` ${colors.dim(safeStringify(data))}`;
|
|
292
|
+
return output;
|
|
293
|
+
}
|
|
294
|
+
function formatJSON(namespace, level, message, data) {
|
|
295
|
+
return safeStringify({
|
|
296
|
+
time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
297
|
+
level,
|
|
298
|
+
name: namespace,
|
|
299
|
+
msg: message,
|
|
300
|
+
...data
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
function matchesNamespaceSet(namespace, set) {
|
|
304
|
+
if (set.has("*")) return true;
|
|
305
|
+
for (const filter of set) if (namespace === filter || namespace.startsWith(filter + ":")) return true;
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
function shouldDebugNamespace(namespace) {
|
|
309
|
+
if (!debugIncludes && !debugExcludes) return true;
|
|
310
|
+
if (debugExcludes && matchesNamespaceSet(namespace, debugExcludes)) return false;
|
|
311
|
+
if (debugIncludes) return matchesNamespaceSet(namespace, debugIncludes);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
/** Resolve a lazy message: if it's a function, call it; otherwise return the string */
|
|
315
|
+
function resolveMessage(msg) {
|
|
316
|
+
return typeof msg === "function" ? msg() : msg;
|
|
317
|
+
}
|
|
318
|
+
function writeLog(namespace, level, message, data) {
|
|
319
|
+
if (!shouldLog(level)) return;
|
|
320
|
+
if (!shouldDebugNamespace(namespace)) return;
|
|
321
|
+
const resolved = resolveMessage(message);
|
|
322
|
+
const contextTags = _getContextTags?.();
|
|
323
|
+
const mergedData = contextTags && Object.keys(contextTags).length > 0 ? {
|
|
324
|
+
...contextTags,
|
|
325
|
+
...data
|
|
326
|
+
} : data;
|
|
327
|
+
const formatted = useJsonFormat() ? formatJSON(namespace, level, resolved, mergedData) : formatConsole(namespace, level, resolved, mergedData);
|
|
328
|
+
for (const w of writers) w(formatted, level);
|
|
329
|
+
if (suppressConsole || outputMode === "writers-only") return;
|
|
330
|
+
if (outputMode === "stderr") {
|
|
331
|
+
writeStderr(formatted);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
switch (level) {
|
|
335
|
+
case "trace":
|
|
336
|
+
case "debug":
|
|
337
|
+
console.debug(formatted);
|
|
338
|
+
break;
|
|
339
|
+
case "info":
|
|
340
|
+
console.info(formatted);
|
|
341
|
+
break;
|
|
342
|
+
case "warn":
|
|
343
|
+
console.warn(formatted);
|
|
344
|
+
break;
|
|
345
|
+
case "error":
|
|
346
|
+
console.error(formatted);
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function writeSpan(namespace, duration, attrs) {
|
|
351
|
+
if (!shouldTraceNamespace(namespace)) return;
|
|
352
|
+
if (!shouldDebugNamespace(namespace)) return;
|
|
353
|
+
const message = `(${duration}ms)`;
|
|
354
|
+
const formatted = useJsonFormat() ? formatJSON(namespace, "span", message, {
|
|
355
|
+
duration,
|
|
356
|
+
...attrs
|
|
357
|
+
}) : formatConsole(namespace, "span", message, {
|
|
358
|
+
duration,
|
|
359
|
+
...attrs
|
|
360
|
+
});
|
|
361
|
+
for (const w of writers) w(formatted, "span");
|
|
362
|
+
if (!suppressConsole) writeStderr(formatted);
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Create a proxy that exposes span metadata as readonly and custom attributes as writable.
|
|
366
|
+
* Shared between core logger spans and worker logger spans.
|
|
367
|
+
*/
|
|
368
|
+
function createSpanDataProxy(getFields, attrs) {
|
|
369
|
+
const READONLY_KEYS = new Set([
|
|
370
|
+
"id",
|
|
371
|
+
"traceId",
|
|
372
|
+
"parentId",
|
|
373
|
+
"startTime",
|
|
374
|
+
"endTime",
|
|
375
|
+
"duration"
|
|
376
|
+
]);
|
|
377
|
+
return new Proxy(attrs, {
|
|
378
|
+
get(_target, prop) {
|
|
379
|
+
if (READONLY_KEYS.has(prop)) return getFields()[prop];
|
|
380
|
+
return attrs[prop];
|
|
381
|
+
},
|
|
382
|
+
set(_target, prop, value) {
|
|
383
|
+
if (READONLY_KEYS.has(prop)) return false;
|
|
384
|
+
attrs[prop] = value;
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function createLoggerImpl(name, props, spanMeta, parentSpanId, traceId, traceSampled = true) {
|
|
390
|
+
const log = (level, msgOrError, data) => {
|
|
391
|
+
if (msgOrError instanceof Error) {
|
|
392
|
+
const err = msgOrError;
|
|
393
|
+
writeLog(name, level, err.message, {
|
|
394
|
+
...props,
|
|
395
|
+
...data,
|
|
396
|
+
error_type: err.name,
|
|
397
|
+
error_stack: err.stack,
|
|
398
|
+
error_code: err.code
|
|
399
|
+
});
|
|
400
|
+
} else writeLog(name, level, msgOrError, {
|
|
401
|
+
...props,
|
|
402
|
+
...data
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
return {
|
|
406
|
+
name,
|
|
407
|
+
props: Object.freeze({ ...props }),
|
|
408
|
+
get spanData() {
|
|
409
|
+
if (!spanMeta) return null;
|
|
410
|
+
return createSpanDataProxy(() => ({
|
|
411
|
+
id: spanMeta.id,
|
|
412
|
+
traceId: spanMeta.traceId,
|
|
413
|
+
parentId: spanMeta.parentId,
|
|
414
|
+
startTime: spanMeta.startTime,
|
|
415
|
+
endTime: spanMeta.endTime,
|
|
416
|
+
duration: spanMeta.endTime !== null ? spanMeta.endTime - spanMeta.startTime : Date.now() - spanMeta.startTime
|
|
417
|
+
}), spanMeta.attrs);
|
|
418
|
+
},
|
|
419
|
+
trace: (msg, data) => log("trace", msg, data),
|
|
420
|
+
debug: (msg, data) => log("debug", msg, data),
|
|
421
|
+
info: (msg, data) => log("info", msg, data),
|
|
422
|
+
warn: (msg, data) => log("warn", msg, data),
|
|
423
|
+
error: (msgOrError, data) => log("error", msgOrError, data),
|
|
424
|
+
logger(namespace, childProps) {
|
|
425
|
+
return createLoggerImpl(namespace ? `${name}:${namespace}` : name, {
|
|
426
|
+
...props,
|
|
427
|
+
...childProps
|
|
428
|
+
}, null, parentSpanId, traceId, traceSampled);
|
|
429
|
+
},
|
|
430
|
+
span(namespace, childProps) {
|
|
431
|
+
const childName = namespace ? `${name}:${namespace}` : name;
|
|
432
|
+
const resolvedChildProps = typeof childProps === "function" ? childProps() : childProps;
|
|
433
|
+
const mergedProps = {
|
|
434
|
+
...props,
|
|
435
|
+
...resolvedChildProps
|
|
436
|
+
};
|
|
437
|
+
const newSpanId = generateSpanId();
|
|
438
|
+
let resolvedParentId = parentSpanId;
|
|
439
|
+
let resolvedTraceId = traceId;
|
|
440
|
+
if (!resolvedParentId && _getContextParent) {
|
|
441
|
+
const ctxParent = _getContextParent();
|
|
442
|
+
if (ctxParent) {
|
|
443
|
+
resolvedParentId = ctxParent.spanId;
|
|
444
|
+
resolvedTraceId = resolvedTraceId || ctxParent.traceId;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
const isNewTrace = !resolvedTraceId;
|
|
448
|
+
const finalTraceId = resolvedTraceId || generateTraceId();
|
|
449
|
+
const sampled = isNewTrace ? shouldSample() : traceSampled;
|
|
450
|
+
const newSpanData = {
|
|
451
|
+
id: newSpanId,
|
|
452
|
+
traceId: finalTraceId,
|
|
453
|
+
parentId: resolvedParentId,
|
|
454
|
+
startTime: Date.now(),
|
|
455
|
+
endTime: null,
|
|
456
|
+
duration: null,
|
|
457
|
+
attrs: {}
|
|
458
|
+
};
|
|
459
|
+
const spanLogger = createLoggerImpl(childName, mergedProps, newSpanData, newSpanId, finalTraceId, sampled);
|
|
460
|
+
_enterContext?.(newSpanId, finalTraceId, resolvedParentId);
|
|
461
|
+
spanLogger[Symbol.dispose] = () => {
|
|
462
|
+
if (newSpanData.endTime !== null) return;
|
|
463
|
+
newSpanData.endTime = Date.now();
|
|
464
|
+
newSpanData.duration = newSpanData.endTime - newSpanData.startTime;
|
|
465
|
+
if (collectSpans) collectedSpans.push(createSpanDataProxy(() => ({
|
|
466
|
+
id: newSpanData.id,
|
|
467
|
+
traceId: newSpanData.traceId,
|
|
468
|
+
parentId: newSpanData.parentId,
|
|
469
|
+
startTime: newSpanData.startTime,
|
|
470
|
+
endTime: newSpanData.endTime,
|
|
471
|
+
duration: newSpanData.duration
|
|
472
|
+
}), { ...newSpanData.attrs }));
|
|
473
|
+
_exitContext?.(newSpanId);
|
|
474
|
+
_ambientRecorder?.recordSpan({
|
|
475
|
+
name: childName,
|
|
476
|
+
durationMs: newSpanData.duration
|
|
477
|
+
});
|
|
478
|
+
if (sampled) writeSpan(childName, newSpanData.duration, {
|
|
479
|
+
span_id: newSpanData.id,
|
|
480
|
+
trace_id: newSpanData.traceId,
|
|
481
|
+
parent_id: newSpanData.parentId,
|
|
482
|
+
...mergedProps,
|
|
483
|
+
...newSpanData.attrs
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
return spanLogger;
|
|
487
|
+
},
|
|
488
|
+
child(context) {
|
|
489
|
+
if (typeof context === "string") return this.logger(context);
|
|
490
|
+
return createLoggerImpl(name, {
|
|
491
|
+
...props,
|
|
492
|
+
...context
|
|
493
|
+
}, null, parentSpanId, traceId, traceSampled);
|
|
494
|
+
},
|
|
495
|
+
end() {
|
|
496
|
+
if (spanMeta?.endTime === null) this[Symbol.dispose]?.();
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Create a plain logger for a component (internal use).
|
|
502
|
+
* For application code, use createLogger() instead which returns undefined for disabled levels.
|
|
503
|
+
*/
|
|
504
|
+
function createPlainLogger(name, props) {
|
|
505
|
+
return createLoggerImpl(name, props || {}, null, null, null);
|
|
506
|
+
}
|
|
507
|
+
const collectedSpans = [];
|
|
508
|
+
let collectSpans = false;
|
|
509
|
+
/** Enable span collection for analysis */
|
|
510
|
+
function startCollecting() {
|
|
511
|
+
collectSpans = true;
|
|
512
|
+
collectedSpans.length = 0;
|
|
513
|
+
}
|
|
514
|
+
/** Stop collecting and return collected spans */
|
|
515
|
+
function stopCollecting() {
|
|
516
|
+
collectSpans = false;
|
|
517
|
+
return [...collectedSpans];
|
|
518
|
+
}
|
|
519
|
+
/** Get collected spans */
|
|
520
|
+
function getCollectedSpans() {
|
|
521
|
+
return [...collectedSpans];
|
|
522
|
+
}
|
|
523
|
+
/** Clear collected spans */
|
|
524
|
+
function clearCollectedSpans() {
|
|
525
|
+
collectedSpans.length = 0;
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Create a logger for a component.
|
|
529
|
+
* Returns undefined for disabled levels - use with optional chaining for zero overhead.
|
|
530
|
+
*
|
|
531
|
+
* Log levels (most → least verbose): trace < debug < info < warn < error < silent
|
|
532
|
+
* Default level: info (trace and debug disabled)
|
|
533
|
+
*
|
|
534
|
+
* @example
|
|
535
|
+
* const log = createLogger('myapp')
|
|
536
|
+
*
|
|
537
|
+
* // All methods support ?. for zero-overhead when disabled
|
|
538
|
+
* log.trace?.(`very verbose: ${expensiveDebug()}`) // Skipped at info level
|
|
539
|
+
* log.debug?.(`debug: ${getState()}`) // Skipped at info level
|
|
540
|
+
* log.info?.('starting') // Enabled at info level
|
|
541
|
+
* log.warn?.('deprecated') // Enabled at info level
|
|
542
|
+
* log.error?.('failed') // Enabled at info level
|
|
543
|
+
*
|
|
544
|
+
* // With -q flag or LOG_LEVEL=warn:
|
|
545
|
+
* log.info?.('starting') // Now skipped - info < warn
|
|
546
|
+
*
|
|
547
|
+
* // With initial props
|
|
548
|
+
* const log = createLogger('myapp', { version: '1.0' })
|
|
549
|
+
*
|
|
550
|
+
* // Create spans
|
|
551
|
+
* {
|
|
552
|
+
* using task = log.span('import', { file: 'data.csv' })
|
|
553
|
+
* task.info?.('importing')
|
|
554
|
+
* task.spanData.count = 42
|
|
555
|
+
* }
|
|
556
|
+
*/
|
|
557
|
+
function createLogger(name, props) {
|
|
558
|
+
const baseLog = createPlainLogger(name, props);
|
|
559
|
+
return new Proxy(baseLog, { get(target, prop) {
|
|
560
|
+
if (prop in LOG_LEVEL_PRIORITY && prop !== "silent") {
|
|
561
|
+
const current = LOG_LEVEL_PRIORITY[currentLogLevel];
|
|
562
|
+
if (LOG_LEVEL_PRIORITY[prop] < current) return;
|
|
563
|
+
}
|
|
564
|
+
return target[prop];
|
|
565
|
+
} });
|
|
566
|
+
}
|
|
567
|
+
//#endregion
|
|
568
|
+
export { setTraceFilter as C, writeSpan as D, stopCollecting as E, setSuppressConsole as S, startCollecting as T, resetIds as _, addWriter as a, setLogLevel as b, createSpanDataProxy as c, getCollectedSpans as d, getDebugFilter as f, getTraceFilter as g, getOutputMode as h, _setContextHooks as i, disableSpans as l, getLogLevel as m, _clearContextHooks as n, clearCollectedSpans as o, getLogFormat as p, _setAmbientRecorder as r, createLogger as s, _ambientRecorder as t, enableSpans as u, setDebugFilter as v, spansAreEnabled as w, setOutputMode as x, setLogFormat as y };
|
|
569
|
+
|
|
570
|
+
//# sourceMappingURL=core-BDFU50FQ.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-BDFU50FQ.mjs","names":["_process","pc"],"sources":["../src/colors.ts","../src/core.ts"],"sourcesContent":["/**\n * Vendored ANSI color functions — replaces picocolors dependency.\n * Supports NO_COLOR, FORCE_COLOR, and TTY detection.\n */\n\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\nconst enabled =\n _process?.env?.[\"FORCE_COLOR\"] !== undefined && _process?.env?.[\"FORCE_COLOR\"] !== \"0\"\n ? true\n : _process?.env?.[\"NO_COLOR\"] !== undefined\n ? false\n : (_process?.stdout?.isTTY ?? false)\n\nfunction wrap(open: string, close: string): (str: string) => string {\n if (!enabled) return (str) => str\n return (str) => open + str + close\n}\n\nexport const colors = {\n dim: wrap(\"\\x1b[2m\", \"\\x1b[22m\"),\n blue: wrap(\"\\x1b[34m\", \"\\x1b[39m\"),\n yellow: wrap(\"\\x1b[33m\", \"\\x1b[39m\"),\n red: wrap(\"\\x1b[31m\", \"\\x1b[39m\"),\n magenta: wrap(\"\\x1b[35m\", \"\\x1b[39m\"),\n cyan: wrap(\"\\x1b[36m\", \"\\x1b[39m\"),\n}\n","/**\n * loggily - Structured logging with spans\n *\n * Logger-first architecture: Span = Logger + Duration\n *\n * @example\n * const log = createLogger('myapp')\n *\n * // Simple logging\n * log.info('starting')\n *\n * // Lazy messages (function not called when level is disabled)\n * log.debug?.(() => `expensive: ${computeState()}`)\n *\n * // Child loggers with context fields\n * const reqLog = log.child({ requestId: 'abc' })\n * reqLog.info('handling request') // includes requestId in every message\n *\n * // With timing (span)\n * {\n * using task = log.span('import', { file: 'data.csv' })\n * task.info('importing')\n * task.spanData.count = 42 // Set span attributes\n * // Auto-disposal on block exit → SPAN myapp:import (15ms)\n * }\n */\n\nimport { colors as pc } from \"./colors.js\"\n\n// ============ Metrics ============\n\n/** Data passed to span recorders on disposal */\nexport interface SpanRecord {\n readonly name: string\n readonly durationMs: number\n}\n\n/** Interface for span duration recording — implemented by createMetricsCollector() in metrics.ts */\nexport interface SpanRecorder {\n recordSpan(data: SpanRecord): void\n}\n\n/**\n * Ambient span recorder — auto-records when TRACE is active.\n * Set by metrics.ts on import; can be replaced for testing.\n * @internal\n */\nexport let _ambientRecorder: SpanRecorder | null = null\nexport function _setAmbientRecorder(recorder: SpanRecorder | null): void {\n _ambientRecorder = recorder\n}\n\n// ============ Runtime Detection ============\n\n/** Cached process reference — undefined in browser/edge runtimes */\nconst _process = typeof process !== \"undefined\" ? process : undefined\n\n/** Read an environment variable, returning undefined in non-Node runtimes */\nfunction getEnv(key: string): string | undefined {\n return _process?.env?.[key]\n}\n\n/** Write to stderr with console.error fallback for non-Node runtimes */\nfunction writeStderr(text: string): void {\n if (_process?.stderr?.write) {\n _process.stderr.write(text + \"\\n\")\n } else {\n console.error(text)\n }\n}\n\n// ============ Types ============\n\n/** Log levels that produce output */\nexport type OutputLogLevel = \"trace\" | \"debug\" | \"info\" | \"warn\" | \"error\"\n\n/** All log levels including silent (for filtering) */\nexport type LogLevel = OutputLogLevel | \"silent\"\n\n/** Message can be a string or a lazy function that returns a string */\nexport type LazyMessage = string | (() => string)\n\n/** Span props can be an object or a lazy function (skipped entirely via ?. when tracing is off) */\nexport type LazyProps = Record<string, unknown> | (() => Record<string, unknown>)\n\n/** Span data accessible via logger.spanData */\nexport interface SpanData {\n readonly id: string\n readonly traceId: string\n readonly parentId: string | null\n readonly startTime: number\n readonly endTime: number | null\n readonly duration: number | null\n /** Custom attributes - set via direct property assignment */\n [key: string]: unknown\n}\n\n/** Logger interface */\nexport interface Logger {\n /** Logger namespace (e.g., 'myapp:import') */\n readonly name: string\n /** Props inherited from parent + own props */\n readonly props: Readonly<Record<string, unknown>>\n /** Span data (non-null for span loggers, null for regular loggers) */\n readonly spanData: SpanData | null\n\n // Logging methods (accept string or lazy () => string)\n trace(message: LazyMessage, data?: Record<string, unknown>): void\n debug(message: LazyMessage, data?: Record<string, unknown>): void\n info(message: LazyMessage, data?: Record<string, unknown>): void\n warn(message: LazyMessage, data?: Record<string, unknown>): void\n error(message: LazyMessage, data?: Record<string, unknown>): void\n /** Error overload - extracts message, stack, code from Error */\n error(error: Error, data?: Record<string, unknown>): void\n\n // Create children\n /** Create child logger (extends namespace, inherits props) */\n logger(namespace?: string, props?: Record<string, unknown>): Logger\n /** Create child span (extends namespace, inherits props, adds timing). Props can be lazy. */\n span(namespace?: string, props?: LazyProps): SpanLogger\n\n /** Create child logger with context fields merged into every message */\n child(context: Record<string, unknown>): Logger\n /** @deprecated Use .logger() instead for namespace-based children */\n child(context: string): Logger\n\n /** End span manually (alternative to using keyword) */\n end(): void\n}\n\n/** Span logger - Logger with active span (spanData is non-null, implements Disposable) */\nexport interface SpanLogger extends Logger, Disposable {\n readonly spanData: SpanData & {\n /** Mutable attributes - set directly */\n [key: string]: unknown\n }\n}\n\n// ============ Writers ============\n\ntype LogWriter = (formatted: string, level: string) => void\nconst writers: LogWriter[] = []\n\n/** Add a writer that receives all formatted log output. Returns unsubscribe. */\nexport function addWriter(writer: LogWriter): () => void {\n writers.push(writer)\n return () => {\n const idx = writers.indexOf(writer)\n if (idx !== -1) writers.splice(idx, 1)\n }\n}\n\nlet suppressConsole = false\n\n/** Suppress console output from the logger (writers still receive output). */\nexport function setSuppressConsole(value: boolean): void {\n suppressConsole = value\n}\n\n/** Output mode for writeLog */\nexport type OutputMode = \"console\" | \"stderr\" | \"writers-only\"\nlet outputMode: OutputMode = \"console\"\n\n/** Set output mode for log messages (not spans — spans always use stderr). */\nexport function setOutputMode(mode: OutputMode): void {\n outputMode = mode\n}\n\n/** Get current output mode */\nexport function getOutputMode(): OutputMode {\n return outputMode\n}\n\n// ============ Configuration ============\n\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n trace: 0,\n debug: 1,\n info: 2,\n warn: 3,\n error: 4,\n silent: 5,\n}\n\n// Initialize from environment\nconst envLogLevel = getEnv(\"LOG_LEVEL\")?.toLowerCase()\nlet currentLogLevel: LogLevel =\n envLogLevel === \"trace\" ||\n envLogLevel === \"debug\" ||\n envLogLevel === \"info\" ||\n envLogLevel === \"warn\" ||\n envLogLevel === \"error\" ||\n envLogLevel === \"silent\"\n ? envLogLevel\n : \"info\"\n\n// Span output control (TRACE=1 or TRACE=myapp,other)\nconst traceEnv = getEnv(\"TRACE\")\nlet spansEnabled = traceEnv === \"1\" || traceEnv === \"true\"\nlet traceFilter: Set<string> | null = null\nif (traceEnv && traceEnv !== \"1\" && traceEnv !== \"true\") {\n traceFilter = new Set(traceEnv.split(\",\").map((s) => s.trim()))\n spansEnabled = true\n}\n\n// Debug namespace filter (DEBUG=myapp or DEBUG=myapp,-myapp:noisy or DEBUG=*)\n// Supports negative patterns with `-` prefix (like the `debug` npm package)\n\n/** Parse a comma-separated namespace filter into include/exclude sets */\nfunction parseNamespaceFilter(input: string[]): {\n includes: Set<string> | null\n excludes: Set<string> | null\n} {\n const includeList: string[] = []\n const excludeList: string[] = []\n for (const part of input) {\n if (part.startsWith(\"-\")) {\n excludeList.push(part.slice(1))\n } else {\n includeList.push(part)\n }\n }\n return {\n includes: includeList.length > 0 ? new Set(includeList) : null,\n excludes: excludeList.length > 0 ? new Set(excludeList) : null,\n }\n}\n\nconst debugEnv = getEnv(\"DEBUG\")\nlet debugIncludes: Set<string> | null = null\nlet debugExcludes: Set<string> | null = null\nif (debugEnv) {\n const parts = debugEnv.split(\",\").map((s) => s.trim())\n const parsed = parseNamespaceFilter(parts)\n debugIncludes = parsed.includes\n // Normalize wildcard variants\n if (debugIncludes && [...debugIncludes].some((p) => p === \"*\" || p === \"1\" || p === \"true\")) {\n debugIncludes = new Set([\"*\"])\n }\n debugExcludes = parsed.excludes\n // Auto-lower log level to at least debug when DEBUG is set\n if (LOG_LEVEL_PRIORITY[currentLogLevel] > LOG_LEVEL_PRIORITY.debug) {\n currentLogLevel = \"debug\"\n }\n}\n\n/** Set minimum log level */\nexport function setLogLevel(level: LogLevel): void {\n currentLogLevel = level\n}\n\n/** Get current log level */\nexport function getLogLevel(): LogLevel {\n return currentLogLevel\n}\n\n/** Enable span output */\nexport function enableSpans(): void {\n spansEnabled = true\n}\n\n/** Disable span output */\nexport function disableSpans(): void {\n spansEnabled = false\n}\n\n/** Check if spans are enabled */\nexport function spansAreEnabled(): boolean {\n return spansEnabled\n}\n\n/**\n * Set trace filter for namespace-based span output control.\n * Only spans matching these namespace prefixes will be output.\n * @param namespaces - Array of namespace prefixes, or null to disable filtering\n */\nexport function setTraceFilter(namespaces: string[] | null): void {\n if (namespaces === null || namespaces.length === 0) {\n traceFilter = null\n } else {\n traceFilter = new Set(namespaces)\n spansEnabled = true\n }\n}\n\n/** Get current trace filter (null means no filtering) */\nexport function getTraceFilter(): string[] | null {\n return traceFilter ? [...traceFilter] : null\n}\n\n/**\n * Set debug namespace filter (like the `debug` npm package).\n * When set, only loggers matching these namespace prefixes produce output.\n * Supports negative patterns with `-` prefix (e.g., [\"-km:noisy\"]).\n * Also ensures log level is at least `debug`.\n * @param namespaces - Array of namespace prefixes (prefix with `-` to exclude), or null to disable\n */\nexport function setDebugFilter(namespaces: string[] | null): void {\n if (namespaces === null || namespaces.length === 0) {\n debugIncludes = null\n debugExcludes = null\n } else {\n const parsed = parseNamespaceFilter(namespaces)\n debugIncludes = parsed.includes\n debugExcludes = parsed.excludes\n if (LOG_LEVEL_PRIORITY[currentLogLevel] > LOG_LEVEL_PRIORITY.debug) {\n currentLogLevel = \"debug\"\n }\n }\n}\n\n/** Get current debug namespace filter (null means no filtering) */\nexport function getDebugFilter(): string[] | null {\n if (!debugIncludes && !debugExcludes) return null\n const result: string[] = []\n if (debugIncludes) result.push(...debugIncludes)\n if (debugExcludes) result.push(...[...debugExcludes].map((e) => `-${e}`))\n return result\n}\n\n// ============ Log Format ============\n\n/** Output format: human-readable console or structured JSON */\nexport type LogFormat = \"console\" | \"json\"\n\n// Initialize from LOG_FORMAT env var, falling back to auto-detect\nconst envLogFormat = getEnv(\"LOG_FORMAT\")?.toLowerCase()\nlet currentLogFormat: LogFormat = envLogFormat === \"json\" ? \"json\" : envLogFormat === \"console\" ? \"console\" : \"console\"\n\n/** Set log output format */\nexport function setLogFormat(format: LogFormat): void {\n currentLogFormat = format\n}\n\n/** Get current log output format */\nexport function getLogFormat(): LogFormat {\n return currentLogFormat\n}\n\n/** Determine whether to use JSON formatting for the current call */\nfunction useJsonFormat(): boolean {\n return currentLogFormat === \"json\" || getEnv(\"NODE_ENV\") === \"production\" || getEnv(\"TRACE_FORMAT\") === \"json\"\n}\n\n// ============ ID Generation (delegated to tracing.ts) ============\n\nimport { generateSpanId, generateTraceId, resetIdCounters, shouldSample } from \"./tracing.js\"\n\n// Reset for testing\nexport function resetIds(): void {\n resetIdCounters()\n}\n\n// ============ Context Propagation Hooks ============\n\n// These are set by context.ts when enableContextPropagation() is called.\n// Kept as nullable callbacks to avoid importing AsyncLocalStorage in browser.\n\n/** Hook to get current span context tags (trace_id, span_id) for auto-tagging logs */\nlet _getContextTags: (() => Record<string, string>) | null = null\n\n/** Hook to get parent span info from async context */\nlet _getContextParent: (() => { spanId: string; traceId: string } | null) | null = null\n\n/** Hook to enter a span context (sets AsyncLocalStorage for the current async scope) */\nlet _enterContext: ((spanId: string, traceId: string, parentId: string | null) => void) | null = null\n\n/** Hook to exit a span context (restores previous context snapshot) */\nlet _exitContext: ((spanId: string) => void) | null = null\n\n/**\n * Register context propagation hooks (called by context.ts).\n * @internal\n */\nexport function _setContextHooks(hooks: {\n getContextTags: () => Record<string, string>\n getContextParent: () => { spanId: string; traceId: string } | null\n enterContext: (spanId: string, traceId: string, parentId: string | null) => void\n exitContext: (spanId: string) => void\n}): void {\n _getContextTags = hooks.getContextTags\n _getContextParent = hooks.getContextParent\n _enterContext = hooks.enterContext\n _exitContext = hooks.exitContext\n}\n\n/**\n * Clear context propagation hooks (called by disableContextPropagation).\n * @internal\n */\nexport function _clearContextHooks(): void {\n _getContextTags = null\n _getContextParent = null\n _enterContext = null\n _exitContext = null\n}\n\n// ============ Formatting ============\n\nfunction shouldLog(level: LogLevel): boolean {\n return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentLogLevel]\n}\n\nfunction shouldTraceNamespace(namespace: string): boolean {\n if (!spansEnabled) return false\n if (!traceFilter) return true\n return matchesNamespaceSet(namespace, traceFilter)\n}\n\n/**\n * Safe JSON.stringify that handles bigint, circular refs, symbols, and Error objects.\n * Prevents crashes from non-serializable values in log data.\n */\nfunction safeStringify(value: unknown): string {\n const seen = new WeakSet()\n return JSON.stringify(value, (_key, val) => {\n if (typeof val === \"bigint\") return val.toString()\n if (typeof val === \"symbol\") return val.toString()\n if (val instanceof Error) return { message: val.message, stack: val.stack, name: val.name }\n if (typeof val === \"object\" && val !== null) {\n if (seen.has(val)) return \"[Circular]\"\n seen.add(val)\n }\n return val\n })\n}\n\nfunction formatConsole(namespace: string, level: string, message: string, data?: Record<string, unknown>): string {\n const time = pc.dim(new Date().toISOString().split(\"T\")[1]?.split(\".\")[0] || \"\")\n\n let levelStr = \"\"\n switch (level) {\n case \"trace\":\n levelStr = pc.dim(\"TRACE\")\n break\n case \"debug\":\n levelStr = pc.dim(\"DEBUG\")\n break\n case \"info\":\n levelStr = pc.blue(\"INFO\")\n break\n case \"warn\":\n levelStr = pc.yellow(\"WARN\")\n break\n case \"error\":\n levelStr = pc.red(\"ERROR\")\n break\n case \"span\":\n levelStr = pc.magenta(\"SPAN\")\n break\n }\n\n const ns = pc.cyan(namespace)\n let output = `${time} ${levelStr} ${ns} ${message}`\n\n if (data && Object.keys(data).length > 0) {\n output += ` ${pc.dim(safeStringify(data))}`\n }\n\n return output\n}\n\nfunction formatJSON(namespace: string, level: string, message: string, data?: Record<string, unknown>): string {\n const entry = {\n time: new Date().toISOString(),\n level,\n name: namespace,\n msg: message,\n ...data,\n }\n return safeStringify(entry)\n}\n\nfunction matchesNamespaceSet(namespace: string, set: Set<string>): boolean {\n if (set.has(\"*\")) return true\n for (const filter of set) {\n if (namespace === filter || namespace.startsWith(filter + \":\")) {\n return true\n }\n }\n return false\n}\n\nfunction shouldDebugNamespace(namespace: string): boolean {\n if (!debugIncludes && !debugExcludes) return true\n // Excludes take priority\n if (debugExcludes && matchesNamespaceSet(namespace, debugExcludes)) {\n return false\n }\n // If includes are set, namespace must match\n if (debugIncludes) return matchesNamespaceSet(namespace, debugIncludes)\n return true\n}\n\n/** Resolve a lazy message: if it's a function, call it; otherwise return the string */\nfunction resolveMessage(msg: LazyMessage): string {\n return typeof msg === \"function\" ? msg() : msg\n}\n\nfunction writeLog(\n namespace: string,\n level: OutputLogLevel,\n message: LazyMessage,\n data?: Record<string, unknown>,\n): void {\n if (!shouldLog(level)) return\n if (!shouldDebugNamespace(namespace)) return\n\n // Resolve lazy message only after level/namespace checks pass\n const resolved = resolveMessage(message)\n\n // Auto-tag with trace/span context when context propagation is enabled\n const contextTags = _getContextTags?.()\n const mergedData = contextTags && Object.keys(contextTags).length > 0 ? { ...contextTags, ...data } : data\n\n const formatted = useJsonFormat()\n ? formatJSON(namespace, level, resolved, mergedData)\n : formatConsole(namespace, level, resolved, mergedData)\n\n for (const w of writers) w(formatted, level)\n\n if (suppressConsole || outputMode === \"writers-only\") return\n\n if (outputMode === \"stderr\") {\n writeStderr(formatted)\n return\n }\n\n // Default: use console methods (captured by Ink's patchConsole for TUI panel)\n switch (level) {\n case \"trace\":\n case \"debug\":\n console.debug(formatted)\n break\n case \"info\":\n console.info(formatted)\n break\n case \"warn\":\n console.warn(formatted)\n break\n case \"error\":\n console.error(formatted)\n break\n }\n}\n\nexport function writeSpan(namespace: string, duration: number, attrs: Record<string, unknown>): void {\n if (!shouldTraceNamespace(namespace)) return\n if (!shouldDebugNamespace(namespace)) return\n\n const message = `(${duration}ms)`\n const formatted = useJsonFormat()\n ? formatJSON(namespace, \"span\", message, { duration, ...attrs })\n : formatConsole(namespace, \"span\", message, { duration, ...attrs })\n\n for (const w of writers) w(formatted, \"span\")\n if (!suppressConsole) writeStderr(formatted)\n}\n\n// ============ Shared SpanData Proxy ============\n\ninterface SpanDataFields {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n}\n\n/**\n * Create a proxy that exposes span metadata as readonly and custom attributes as writable.\n * Shared between core logger spans and worker logger spans.\n */\nexport function createSpanDataProxy(getFields: () => SpanDataFields, attrs: Record<string, unknown>): SpanData {\n const READONLY_KEYS = new Set([\"id\", \"traceId\", \"parentId\", \"startTime\", \"endTime\", \"duration\"])\n return new Proxy(attrs, {\n get(_target, prop) {\n if (READONLY_KEYS.has(prop as string)) {\n return getFields()[prop as keyof SpanDataFields]\n }\n return attrs[prop as string]\n },\n set(_target, prop, value) {\n if (READONLY_KEYS.has(prop as string)) {\n return false\n }\n attrs[prop as string] = value\n return true\n },\n }) as SpanData\n}\n\n// ============ Implementation ============\n\ninterface MutableSpanData {\n id: string\n traceId: string\n parentId: string | null\n startTime: number\n endTime: number | null\n duration: number | null\n attrs: Record<string, unknown>\n}\n\nfunction createLoggerImpl(\n name: string,\n props: Record<string, unknown>,\n spanMeta: MutableSpanData | null,\n parentSpanId: string | null,\n traceId: string | null,\n traceSampled: boolean = true,\n): Logger {\n const log = (level: OutputLogLevel, msgOrError: LazyMessage | Error, data?: Record<string, unknown>): void => {\n if (msgOrError instanceof Error) {\n const err = msgOrError\n writeLog(name, level, err.message, {\n ...props,\n ...data,\n error_type: err.name,\n error_stack: err.stack,\n error_code: (err as { code?: string }).code,\n })\n } else {\n writeLog(name, level, msgOrError, { ...props, ...data })\n }\n }\n\n const logger: Logger = {\n name,\n props: Object.freeze({ ...props }),\n\n get spanData(): SpanData | null {\n if (!spanMeta) return null\n return createSpanDataProxy(\n () => ({\n id: spanMeta.id,\n traceId: spanMeta.traceId,\n parentId: spanMeta.parentId,\n startTime: spanMeta.startTime,\n endTime: spanMeta.endTime,\n duration: spanMeta.endTime !== null ? spanMeta.endTime - spanMeta.startTime : Date.now() - spanMeta.startTime,\n }),\n spanMeta.attrs,\n )\n },\n\n trace: (msg, data) => log(\"trace\", msg, data),\n debug: (msg, data) => log(\"debug\", msg, data),\n info: (msg, data) => log(\"info\", msg, data),\n warn: (msg, data) => log(\"warn\", msg, data),\n error: (msgOrError, data) => log(\"error\", msgOrError as string, data),\n\n logger(namespace?: string, childProps?: Record<string, unknown>): Logger {\n const childName = namespace ? `${name}:${namespace}` : name\n const mergedProps = { ...props, ...childProps }\n return createLoggerImpl(childName, mergedProps, null, parentSpanId, traceId, traceSampled)\n },\n\n span(namespace?: string, childProps?: LazyProps): SpanLogger {\n const childName = namespace ? `${name}:${namespace}` : name\n const resolvedChildProps = typeof childProps === \"function\" ? childProps() : childProps\n const mergedProps = { ...props, ...resolvedChildProps }\n const newSpanId = generateSpanId()\n\n // Resolve parent from context propagation if not explicitly set\n let resolvedParentId = parentSpanId\n let resolvedTraceId = traceId\n\n if (!resolvedParentId && _getContextParent) {\n const ctxParent = _getContextParent()\n if (ctxParent) {\n resolvedParentId = ctxParent.spanId\n resolvedTraceId = resolvedTraceId || ctxParent.traceId\n }\n }\n\n // Determine trace ID — generate new one if starting a new trace\n const isNewTrace = !resolvedTraceId\n const finalTraceId = resolvedTraceId || generateTraceId()\n\n // Head-based sampling: inherit from parent, or decide at trace creation\n const sampled = isNewTrace ? shouldSample() : traceSampled\n\n const newSpanData: MutableSpanData = {\n id: newSpanId,\n traceId: finalTraceId,\n parentId: resolvedParentId,\n startTime: Date.now(),\n endTime: null,\n duration: null,\n attrs: {},\n }\n\n const spanLogger = createLoggerImpl(\n childName,\n mergedProps,\n newSpanData,\n newSpanId,\n finalTraceId,\n sampled,\n ) as SpanLogger\n\n // Enter span context for async propagation (if enabled)\n _enterContext?.(newSpanId, finalTraceId, resolvedParentId)\n\n // Add disposal\n ;(spanLogger as unknown as { [Symbol.dispose]: () => void })[Symbol.dispose] = () => {\n if (newSpanData.endTime !== null) return // Already disposed\n\n newSpanData.endTime = Date.now()\n newSpanData.duration = newSpanData.endTime - newSpanData.startTime\n\n // Collect span data if collection is active\n if (collectSpans) {\n collectedSpans.push(\n createSpanDataProxy(\n () => ({\n id: newSpanData.id,\n traceId: newSpanData.traceId,\n parentId: newSpanData.parentId,\n startTime: newSpanData.startTime,\n endTime: newSpanData.endTime,\n duration: newSpanData.duration,\n }),\n { ...newSpanData.attrs },\n ),\n )\n }\n\n // Exit span context (restore previous context snapshot)\n _exitContext?.(newSpanId)\n\n // Record to ambient recorder (auto-active when TRACE is on, set by metrics.ts)\n _ambientRecorder?.recordSpan({ name: childName, durationMs: newSpanData.duration })\n\n // Only emit span if sampled\n if (sampled) {\n writeSpan(childName, newSpanData.duration, {\n span_id: newSpanData.id,\n trace_id: newSpanData.traceId,\n parent_id: newSpanData.parentId,\n ...mergedProps,\n ...newSpanData.attrs,\n })\n }\n }\n\n return spanLogger\n },\n\n child(context: string | Record<string, unknown>): Logger {\n if (typeof context === \"string\") {\n // Deprecated string overload - use .logger() instead\n return this.logger(context)\n }\n // Context object overload: merge context fields into props\n return createLoggerImpl(name, { ...props, ...context }, null, parentSpanId, traceId, traceSampled)\n },\n\n end(): void {\n if (spanMeta?.endTime === null) {\n ;(this as unknown as { [Symbol.dispose]: () => void })[Symbol.dispose]?.()\n }\n },\n }\n\n return logger\n}\n\n/**\n * Create a plain logger for a component (internal use).\n * For application code, use createLogger() instead which returns undefined for disabled levels.\n */\nfunction createPlainLogger(name: string, props?: Record<string, unknown>): Logger {\n return createLoggerImpl(name, props || {}, null, null, null)\n}\n\n// ============ Collected Spans (for analysis) ============\n\nconst collectedSpans: SpanData[] = []\nlet collectSpans = false\n\n/** Enable span collection for analysis */\nexport function startCollecting(): void {\n collectSpans = true\n collectedSpans.length = 0\n}\n\n/** Stop collecting and return collected spans */\nexport function stopCollecting(): SpanData[] {\n collectSpans = false\n return [...collectedSpans]\n}\n\n/** Get collected spans */\nexport function getCollectedSpans(): SpanData[] {\n return [...collectedSpans]\n}\n\n/** Clear collected spans */\nexport function clearCollectedSpans(): void {\n collectedSpans.length = 0\n}\n\n// ============ Conditional Logger (Zero-Overhead Pattern) ============\n\n/**\n * Logger with optional methods — returns undefined for disabled levels.\n * Use with optional chaining: `log.debug?.(\"msg\")` for zero-overhead when disabled.\n *\n * Defined as an explicit interface (not Omit<Logger,...>) so that\n * oxlint's type-aware mode can resolve it without advanced type inference.\n */\nexport interface ConditionalLogger {\n readonly name: string\n readonly props: Readonly<Record<string, unknown>>\n readonly spanData: SpanData | null\n\n trace?: (message: LazyMessage, data?: Record<string, unknown>) => void\n debug?: (message: LazyMessage, data?: Record<string, unknown>) => void\n info?: (message: LazyMessage, data?: Record<string, unknown>) => void\n warn?: (message: LazyMessage, data?: Record<string, unknown>) => void\n error?: {\n (message: LazyMessage, data?: Record<string, unknown>): void\n (error: Error, data?: Record<string, unknown>): void\n }\n\n logger(namespace?: string, props?: Record<string, unknown>): Logger\n span(namespace?: string, props?: LazyProps): SpanLogger\n child(context: Record<string, unknown>): Logger\n child(context: string): Logger\n end(): void\n}\n\n/**\n * Create a logger for a component.\n * Returns undefined for disabled levels - use with optional chaining for zero overhead.\n *\n * Log levels (most → least verbose): trace < debug < info < warn < error < silent\n * Default level: info (trace and debug disabled)\n *\n * @example\n * const log = createLogger('myapp')\n *\n * // All methods support ?. for zero-overhead when disabled\n * log.trace?.(`very verbose: ${expensiveDebug()}`) // Skipped at info level\n * log.debug?.(`debug: ${getState()}`) // Skipped at info level\n * log.info?.('starting') // Enabled at info level\n * log.warn?.('deprecated') // Enabled at info level\n * log.error?.('failed') // Enabled at info level\n *\n * // With -q flag or LOG_LEVEL=warn:\n * log.info?.('starting') // Now skipped - info < warn\n *\n * // With initial props\n * const log = createLogger('myapp', { version: '1.0' })\n *\n * // Create spans\n * {\n * using task = log.span('import', { file: 'data.csv' })\n * task.info?.('importing')\n * task.spanData.count = 42\n * }\n */\nexport function createLogger(name: string, props?: Record<string, unknown>): ConditionalLogger {\n const baseLog = createPlainLogger(name, props)\n\n return new Proxy(baseLog as ConditionalLogger, {\n get(target, prop: string) {\n if (prop in LOG_LEVEL_PRIORITY && prop !== \"silent\") {\n const current = LOG_LEVEL_PRIORITY[currentLogLevel]\n if (LOG_LEVEL_PRIORITY[prop as keyof typeof LOG_LEVEL_PRIORITY] < current) {\n return undefined\n }\n }\n return (target as unknown as Record<string, unknown>)[prop]\n },\n })\n}\n"],"mappings":"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"}
|