loggily 0.6.0 → 0.6.2

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,46 +0,0 @@
1
- //#region src/file-writer.d.ts
2
- /**
3
- * File writer for loggily — Node.js/Bun only.
4
- *
5
- * Separated from core logger to allow tree-shaking in browser bundles.
6
- * Uses dynamic import("node:fs") to avoid static dependency on Node APIs.
7
- */
8
- /** Options for creating an async buffered file writer */
9
- interface FileWriterOptions {
10
- /** Buffer size threshold in bytes before flushing (default: 4096) */
11
- bufferSize?: number;
12
- /** Flush interval in milliseconds (default: 100) */
13
- flushInterval?: number;
14
- }
15
- /** An async buffered file writer with automatic flushing */
16
- interface FileWriter {
17
- /** Write a line to the buffer (appends newline) */
18
- write(line: string): void;
19
- /** Flush the buffer immediately */
20
- flush(): void;
21
- /** Close the writer and flush remaining buffer */
22
- close(): void;
23
- }
24
- /**
25
- * Create an async buffered file writer for log output.
26
- * Buffers writes and flushes on size threshold or interval.
27
- * Registers a process.on('exit') handler to flush remaining buffer.
28
- *
29
- * **Node.js/Bun only** — not available in browser environments.
30
- *
31
- * @param filePath - Path to the log file (opened in append mode)
32
- * @param options - Buffer size and flush interval configuration
33
- * @returns FileWriter with write, flush, and close methods
34
- *
35
- * @example
36
- * const writer = createFileWriter('/tmp/app.log')
37
- * const unsubscribe = addWriter((formatted) => writer.write(formatted))
38
- *
39
- * // On shutdown:
40
- * unsubscribe()
41
- * writer.close()
42
- */
43
- declare function createFileWriter(filePath: string, options?: FileWriterOptions): FileWriter;
44
- //#endregion
45
- export { FileWriterOptions as n, createFileWriter as r, FileWriter as t };
46
- //# sourceMappingURL=file-writer-BuQGFGRs.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-writer-BuQGFGRs.d.mts","names":[],"sources":["../src/file-writer.ts"],"mappings":";;AAUA;;;;;AAQA;AAAA,UARiB,iBAAA;;EAEf,UAAA;EAQA;EANA,aAAA;AAAA;;UAIe,UAAA;EAMV;EAJL,KAAA,CAAM,IAAA;EA0BwB;EAxB9B,KAAA;EAwB6F;EAtB7F,KAAA;AAAA;;;;;;;;;;;;;;;;;;;;iBAsBc,gBAAA,CAAiB,QAAA,UAAkB,OAAA,GAAS,iBAAA,GAAyB,UAAA"}
@@ -1,2 +0,0 @@
1
- import { n as FileWriterOptions, r as createFileWriter, t as FileWriter } from "./file-writer-BuQGFGRs.mjs";
2
- export { FileWriter, FileWriterOptions, createFileWriter };
@@ -1,75 +0,0 @@
1
- import { closeSync, openSync, writeSync } from "node:fs";
2
- //#region src/file-writer.ts
3
- /**
4
- * File writer for loggily — Node.js/Bun only.
5
- *
6
- * Separated from core logger to allow tree-shaking in browser bundles.
7
- * Uses dynamic import("node:fs") to avoid static dependency on Node APIs.
8
- */
9
- /**
10
- * Create an async buffered file writer for log output.
11
- * Buffers writes and flushes on size threshold or interval.
12
- * Registers a process.on('exit') handler to flush remaining buffer.
13
- *
14
- * **Node.js/Bun only** — not available in browser environments.
15
- *
16
- * @param filePath - Path to the log file (opened in append mode)
17
- * @param options - Buffer size and flush interval configuration
18
- * @returns FileWriter with write, flush, and close methods
19
- *
20
- * @example
21
- * const writer = createFileWriter('/tmp/app.log')
22
- * const unsubscribe = addWriter((formatted) => writer.write(formatted))
23
- *
24
- * // On shutdown:
25
- * unsubscribe()
26
- * writer.close()
27
- */
28
- function createFileWriter(filePath, options = {}) {
29
- const bufferSize = options.bufferSize ?? 4096;
30
- const flushInterval = options.flushInterval ?? 100;
31
- let buffer = "";
32
- let fd = null;
33
- let timer = null;
34
- let closed = false;
35
- fd = openSync(filePath, "a");
36
- /** Flush buffer contents to disk synchronously */
37
- function flush() {
38
- if (buffer.length === 0 || fd === null) return;
39
- writeSync(fd, buffer);
40
- buffer = "";
41
- }
42
- timer = setInterval(flush, flushInterval);
43
- if (timer && typeof timer === "object" && "unref" in timer) timer.unref();
44
- const exitHandler = () => flush();
45
- process.on("exit", exitHandler);
46
- return {
47
- write(line) {
48
- if (closed) return;
49
- buffer += line + "\n";
50
- if (buffer.length >= bufferSize) flush();
51
- },
52
- flush,
53
- close() {
54
- if (closed) return;
55
- closed = true;
56
- if (timer !== null) {
57
- clearInterval(timer);
58
- timer = null;
59
- }
60
- try {
61
- flush();
62
- } catch {} finally {
63
- if (fd !== null) {
64
- closeSync(fd);
65
- fd = null;
66
- }
67
- process.removeListener("exit", exitHandler);
68
- }
69
- }
70
- };
71
- }
72
- //#endregion
73
- export { createFileWriter };
74
-
75
- //# sourceMappingURL=file-writer.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-writer.mjs","names":[],"sources":["../src/file-writer.ts"],"sourcesContent":["/**\n * File writer for loggily — Node.js/Bun only.\n *\n * Separated from core logger to allow tree-shaking in browser bundles.\n * Uses dynamic import(\"node:fs\") to avoid static dependency on Node APIs.\n */\n\nimport { openSync, writeSync, closeSync } from \"node:fs\"\n\n/** Options for creating an async buffered file writer */\nexport interface FileWriterOptions {\n /** Buffer size threshold in bytes before flushing (default: 4096) */\n bufferSize?: number\n /** Flush interval in milliseconds (default: 100) */\n flushInterval?: number\n}\n\n/** An async buffered file writer with automatic flushing */\nexport interface FileWriter {\n /** Write a line to the buffer (appends newline) */\n write(line: string): void\n /** Flush the buffer immediately */\n flush(): void\n /** Close the writer and flush remaining buffer */\n close(): void\n}\n\n/**\n * Create an async buffered file writer for log output.\n * Buffers writes and flushes on size threshold or interval.\n * Registers a process.on('exit') handler to flush remaining buffer.\n *\n * **Node.js/Bun only** — not available in browser environments.\n *\n * @param filePath - Path to the log file (opened in append mode)\n * @param options - Buffer size and flush interval configuration\n * @returns FileWriter with write, flush, and close methods\n *\n * @example\n * const writer = createFileWriter('/tmp/app.log')\n * const unsubscribe = addWriter((formatted) => writer.write(formatted))\n *\n * // On shutdown:\n * unsubscribe()\n * writer.close()\n */\nexport function createFileWriter(filePath: string, options: FileWriterOptions = {}): FileWriter {\n const bufferSize = options.bufferSize ?? 4096\n const flushInterval = options.flushInterval ?? 100\n\n let buffer = \"\"\n let fd: number | null = null\n let timer: ReturnType<typeof setInterval> | null = null\n let closed = false\n\n // Open file in append mode\n fd = openSync(filePath, \"a\")\n\n /** Flush buffer contents to disk synchronously */\n function flush(): void {\n if (buffer.length === 0 || fd === null) return\n const data = buffer\n writeSync(fd, data)\n buffer = \"\"\n }\n\n // Set up periodic flush\n timer = setInterval(flush, flushInterval)\n // Don't let the timer keep the process alive\n if (timer && typeof timer === \"object\" && \"unref\" in timer) {\n ;(timer as { unref(): void }).unref()\n }\n\n // Flush on process exit to avoid data loss\n const exitHandler = (): void => flush()\n process.on(\"exit\", exitHandler)\n\n return {\n write(line: string): void {\n if (closed) return\n buffer += line + \"\\n\"\n if (buffer.length >= bufferSize) {\n flush()\n }\n },\n\n flush,\n\n close(): void {\n if (closed) return\n closed = true\n if (timer !== null) {\n clearInterval(timer)\n timer = null\n }\n try {\n flush()\n } catch {\n // Swallow flush errors during close — data loss is unavoidable\n // at this point, but we must still release the fd and exit handler.\n } finally {\n if (fd !== null) {\n closeSync(fd)\n fd = null\n }\n process.removeListener(\"exit\", exitHandler)\n }\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,iBAAiB,UAAkB,UAA6B,EAAE,EAAc;CAC9F,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,gBAAgB,QAAQ,iBAAiB;CAE/C,IAAI,SAAS;CACb,IAAI,KAAoB;CACxB,IAAI,QAA+C;CACnD,IAAI,SAAS;AAGb,MAAK,SAAS,UAAU,IAAI;;CAG5B,SAAS,QAAc;AACrB,MAAI,OAAO,WAAW,KAAK,OAAO,KAAM;AAExC,YAAU,IADG,OACM;AACnB,WAAS;;AAIX,SAAQ,YAAY,OAAO,cAAc;AAEzC,KAAI,SAAS,OAAO,UAAU,YAAY,WAAW,MACjD,OAA4B,OAAO;CAIvC,MAAM,oBAA0B,OAAO;AACvC,SAAQ,GAAG,QAAQ,YAAY;AAE/B,QAAO;EACL,MAAM,MAAoB;AACxB,OAAI,OAAQ;AACZ,aAAU,OAAO;AACjB,OAAI,OAAO,UAAU,WACnB,QAAO;;EAIX;EAEA,QAAc;AACZ,OAAI,OAAQ;AACZ,YAAS;AACT,OAAI,UAAU,MAAM;AAClB,kBAAc,MAAM;AACpB,YAAQ;;AAEV,OAAI;AACF,WAAO;WACD,WAGE;AACR,QAAI,OAAO,MAAM;AACf,eAAU,GAAG;AACb,UAAK;;AAEP,YAAQ,eAAe,QAAQ,YAAY;;;EAGhD"}
@@ -1,48 +0,0 @@
1
- import { d as SpanRecord, f as SpanRecorder, t as ConditionalLogger } from "./core-7D7sstHl.mjs";
2
-
3
- //#region src/metrics.d.ts
4
- interface SpanStats {
5
- count: number;
6
- min: number;
7
- max: number;
8
- mean: number;
9
- p50: number;
10
- p95: number;
11
- p99: number;
12
- total: number;
13
- }
14
- interface MetricsCollector extends SpanRecorder {
15
- /** Get stats for a specific span namespace */
16
- stats(name: string): SpanStats | undefined;
17
- /** Get stats for all recorded namespaces */
18
- all(): Map<string, SpanStats>;
19
- /** Format a human-readable summary */
20
- summary(): string;
21
- /** Reset all collected data */
22
- reset(): void;
23
- }
24
- declare function createMetricsCollector(maxEntries?: number): MetricsCollector;
25
- /** Get aggregated span stats (from ambient collector). */
26
- declare function spanStats(): Map<string, SpanStats>;
27
- /** Get the ambient collector's formatted summary. */
28
- declare function spanSummary(): string;
29
- /** Reset the ambient collector. */
30
- declare function resetSpanStats(): void;
31
- /**
32
- * Compose a logger with a metrics collector.
33
- * Returns a curried wrapper: `withMetrics(collector?)(logger)`
34
- *
35
- * - No arg: uses the built-in ambient collector
36
- * - Custom collector: records to your collector
37
- * - Stackable: `withMetrics(a)(withMetrics(b)(logger))` fans out to both
38
- *
39
- * @example
40
- * ```typescript
41
- * const log = withMetrics()(createLogger("myapp"))
42
- * const log = withMetrics(myCollector)(createLogger("myapp"))
43
- * ```
44
- */
45
- declare function withMetrics(collector?: SpanRecorder): (logger: ConditionalLogger) => ConditionalLogger;
46
- //#endregion
47
- export { MetricsCollector, type SpanRecord, type SpanRecorder, SpanStats, createMetricsCollector, resetSpanStats, spanStats, spanSummary, withMetrics };
48
- //# sourceMappingURL=metrics.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metrics.d.mts","names":[],"sources":["../src/metrics.ts"],"mappings":";;;UAkCiB,SAAA;EACf,KAAA;EACA,GAAA;EACA,GAAA;EACA,IAAA;EACA,GAAA;EACA,GAAA;EACA,GAAA;EACA,KAAA;AAAA;AAAA,UA0Be,gBAAA,SAAyB,YAAA;EAIjC;EAFP,KAAA,CAAM,IAAA,WAAe,SAAA;EAIrB;EAFA,GAAA,IAAO,GAAA,SAAY,SAAA;EAId;EAFL,OAAA;EAKc;EAHd,KAAA;AAAA;AAAA,iBAGc,sBAAA,CAAuB,UAAA,YAAoB,gBAAA;;iBAuD3C,SAAA,CAAA,GAAa,GAAA,SAAY,SAAA;;iBAKzB,WAAA,CAAA;;iBAKA,cAAA,CAAA;AALhB;;;;;AAKA;;;;;AAoBA;;;;AAzBA,iBAyBgB,WAAA,CAAY,SAAA,GAAY,YAAA,IAAgB,MAAA,EAAQ,iBAAA,KAAsB,iBAAA"}
package/dist/metrics.mjs DELETED
@@ -1,130 +0,0 @@
1
- import { r as _setAmbientRecorder } from "./core-BDFU50FQ.mjs";
2
- //#region src/metrics.ts
3
- /**
4
- * Metrics collection for loggily spans.
5
- *
6
- * Two modes:
7
- * - **Ambient**: import this module → spans auto-record when TRACE is active
8
- * - **Explicit**: `withMetrics(collector?)(logger)` for custom collection
9
- *
10
- * @example
11
- * ```typescript
12
- * import { spanStats } from "loggily/metrics"
13
- * // TRACE=myapp bun run app
14
- * // → on exit: spanStats() returns aggregated p50/p95/p99
15
- *
16
- * // Custom collector:
17
- * import { withMetrics, createMetricsCollector } from "loggily/metrics"
18
- * const log = withMetrics()(createLogger("myapp"))
19
- * ```
20
- */
21
- function percentile(sorted, p) {
22
- if (sorted.length === 0) return 0;
23
- return sorted[Math.min(Math.floor(sorted.length * p), sorted.length - 1)];
24
- }
25
- function computeStats(durations) {
26
- const sorted = [...durations].sort((a, b) => a - b);
27
- const total = sorted.reduce((sum, d) => sum + d, 0);
28
- return {
29
- count: sorted.length,
30
- min: sorted[0] ?? 0,
31
- max: sorted[sorted.length - 1] ?? 0,
32
- mean: sorted.length > 0 ? total / sorted.length : 0,
33
- p50: percentile(sorted, .5),
34
- p95: percentile(sorted, .95),
35
- p99: percentile(sorted, .99),
36
- total
37
- };
38
- }
39
- function createMetricsCollector(maxEntries = 1e3) {
40
- const store = /* @__PURE__ */ new Map();
41
- return {
42
- recordSpan(data) {
43
- let arr = store.get(data.name);
44
- if (!arr) {
45
- arr = [];
46
- store.set(data.name, arr);
47
- }
48
- arr.push(data.durationMs);
49
- if (arr.length > maxEntries) arr.shift();
50
- },
51
- stats(name) {
52
- const arr = store.get(name);
53
- if (!arr || arr.length === 0) return void 0;
54
- return computeStats(arr);
55
- },
56
- all() {
57
- const result = /* @__PURE__ */ new Map();
58
- for (const [name, durations] of store) if (durations.length > 0) result.set(name, computeStats(durations));
59
- return result;
60
- },
61
- summary() {
62
- const entries = [...this.all().entries()];
63
- if (entries.length === 0) return "(no span data)";
64
- return entries.map(([name, s]) => `${name}: ${s.count} spans, mean=${s.mean.toFixed(1)}ms, p50=${s.p50.toFixed(1)}ms, p95=${s.p95.toFixed(1)}ms, p99=${s.p99.toFixed(1)}ms`).join("\n");
65
- },
66
- reset() {
67
- store.clear();
68
- }
69
- };
70
- }
71
- const _ambient = createMetricsCollector();
72
- _setAmbientRecorder(_ambient);
73
- /** Get aggregated span stats (from ambient collector). */
74
- function spanStats() {
75
- return _ambient.all();
76
- }
77
- /** Get the ambient collector's formatted summary. */
78
- function spanSummary() {
79
- return _ambient.summary();
80
- }
81
- /** Reset the ambient collector. */
82
- function resetSpanStats() {
83
- _ambient.reset();
84
- }
85
- /**
86
- * Compose a logger with a metrics collector.
87
- * Returns a curried wrapper: `withMetrics(collector?)(logger)`
88
- *
89
- * - No arg: uses the built-in ambient collector
90
- * - Custom collector: records to your collector
91
- * - Stackable: `withMetrics(a)(withMetrics(b)(logger))` fans out to both
92
- *
93
- * @example
94
- * ```typescript
95
- * const log = withMetrics()(createLogger("myapp"))
96
- * const log = withMetrics(myCollector)(createLogger("myapp"))
97
- * ```
98
- */
99
- function withMetrics(collector) {
100
- const recorder = collector ?? _ambient;
101
- return (logger) => {
102
- return new Proxy(logger, { get(target, prop) {
103
- if (prop === "span") {
104
- const originalSpan = target.span;
105
- if (!originalSpan) return void 0;
106
- return (namespace, props) => {
107
- const span = originalSpan.call(target, namespace, props);
108
- const originalDispose = span[Symbol.dispose];
109
- span[Symbol.dispose] = () => {
110
- originalDispose.call(span);
111
- if (span.spanData?.duration != null) recorder.recordSpan({
112
- name: span.name,
113
- durationMs: span.spanData.duration
114
- });
115
- };
116
- return span;
117
- };
118
- }
119
- if (prop === "logger") return (namespace, childProps) => {
120
- const child = target.logger(namespace, childProps);
121
- return withMetrics(recorder)(child);
122
- };
123
- return target[prop];
124
- } });
125
- };
126
- }
127
- //#endregion
128
- export { createMetricsCollector, resetSpanStats, spanStats, spanSummary, withMetrics };
129
-
130
- //# sourceMappingURL=metrics.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"metrics.mjs","names":[],"sources":["../src/metrics.ts"],"sourcesContent":["/**\n * Metrics collection for loggily spans.\n *\n * Two modes:\n * - **Ambient**: import this module → spans auto-record when TRACE is active\n * - **Explicit**: `withMetrics(collector?)(logger)` for custom collection\n *\n * @example\n * ```typescript\n * import { spanStats } from \"loggily/metrics\"\n * // TRACE=myapp bun run app\n * // → on exit: spanStats() returns aggregated p50/p95/p99\n *\n * // Custom collector:\n * import { withMetrics, createMetricsCollector } from \"loggily/metrics\"\n * const log = withMetrics()(createLogger(\"myapp\"))\n * ```\n */\n\nimport {\n type SpanRecorder,\n type SpanRecord,\n type ConditionalLogger,\n type Logger,\n type LazyProps,\n type SpanLogger,\n _setAmbientRecorder,\n spansAreEnabled,\n} from \"./core.js\"\n\nexport type { SpanRecorder, SpanRecord }\n\n// ============ Stats ============\n\nexport interface SpanStats {\n count: number\n min: number\n max: number\n mean: number\n p50: number\n p95: number\n p99: number\n total: number\n}\n\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0\n const idx = Math.min(Math.floor(sorted.length * p), sorted.length - 1)\n return sorted[idx]!\n}\n\nfunction computeStats(durations: number[]): SpanStats {\n const sorted = [...durations].sort((a, b) => a - b)\n const total = sorted.reduce((sum, d) => sum + d, 0)\n return {\n count: sorted.length,\n min: sorted[0] ?? 0,\n max: sorted[sorted.length - 1] ?? 0,\n mean: sorted.length > 0 ? total / sorted.length : 0,\n p50: percentile(sorted, 0.5),\n p95: percentile(sorted, 0.95),\n p99: percentile(sorted, 0.99),\n total,\n }\n}\n\n// ============ Collector ============\n\nexport interface MetricsCollector extends SpanRecorder {\n /** Get stats for a specific span namespace */\n stats(name: string): SpanStats | undefined\n /** Get stats for all recorded namespaces */\n all(): Map<string, SpanStats>\n /** Format a human-readable summary */\n summary(): string\n /** Reset all collected data */\n reset(): void\n}\n\nexport function createMetricsCollector(maxEntries = 1000): MetricsCollector {\n const store = new Map<string, number[]>()\n\n return {\n recordSpan(data: SpanRecord): void {\n let arr = store.get(data.name)\n if (!arr) {\n arr = []\n store.set(data.name, arr)\n }\n arr.push(data.durationMs)\n // Bound memory: keep last N entries per namespace\n if (arr.length > maxEntries) arr.shift()\n },\n\n stats(name: string): SpanStats | undefined {\n const arr = store.get(name)\n if (!arr || arr.length === 0) return undefined\n return computeStats(arr)\n },\n\n all(): Map<string, SpanStats> {\n const result = new Map<string, SpanStats>()\n for (const [name, durations] of store) {\n if (durations.length > 0) result.set(name, computeStats(durations))\n }\n return result\n },\n\n summary(): string {\n const entries = [...this.all().entries()]\n if (entries.length === 0) return \"(no span data)\"\n const lines = entries.map(\n ([name, s]) =>\n `${name}: ${s.count} spans, mean=${s.mean.toFixed(1)}ms, p50=${s.p50.toFixed(1)}ms, p95=${s.p95.toFixed(1)}ms, p99=${s.p99.toFixed(1)}ms`,\n )\n return lines.join(\"\\n\")\n },\n\n reset(): void {\n store.clear()\n },\n }\n}\n\n// ============ Ambient Collector ============\n\nconst _ambient = createMetricsCollector()\n\n// Auto-activate ambient recording.\n// Always set — the cost is one ?.recordSpan() call per span (negligible).\n// The TRACE gate already controls whether spans are *created* at all.\n_setAmbientRecorder(_ambient)\n\n/** Get aggregated span stats (from ambient collector). */\nexport function spanStats(): Map<string, SpanStats> {\n return _ambient.all()\n}\n\n/** Get the ambient collector's formatted summary. */\nexport function spanSummary(): string {\n return _ambient.summary()\n}\n\n/** Reset the ambient collector. */\nexport function resetSpanStats(): void {\n _ambient.reset()\n}\n\n// ============ withMetrics ============\n\n/**\n * Compose a logger with a metrics collector.\n * Returns a curried wrapper: `withMetrics(collector?)(logger)`\n *\n * - No arg: uses the built-in ambient collector\n * - Custom collector: records to your collector\n * - Stackable: `withMetrics(a)(withMetrics(b)(logger))` fans out to both\n *\n * @example\n * ```typescript\n * const log = withMetrics()(createLogger(\"myapp\"))\n * const log = withMetrics(myCollector)(createLogger(\"myapp\"))\n * ```\n */\nexport function withMetrics(collector?: SpanRecorder): (logger: ConditionalLogger) => ConditionalLogger {\n const recorder = collector ?? _ambient\n\n return (logger: ConditionalLogger): ConditionalLogger => {\n // Wrap the logger's span method to intercept disposal\n return new Proxy(logger, {\n get(target, prop: string | symbol) {\n if (prop === \"span\") {\n const originalSpan = target.span\n if (!originalSpan) return undefined // TRACE off — preserve ?. behavior\n return (namespace?: string, props?: LazyProps): SpanLogger => {\n const span = originalSpan.call(target, namespace, props)\n // Wrap disposal to record to our collector\n const originalDispose = (span as unknown as { [Symbol.dispose]: () => void })[Symbol.dispose]\n ;(span as unknown as { [Symbol.dispose]: () => void })[Symbol.dispose] = () => {\n originalDispose.call(span)\n // After original disposal computed duration, record it\n if (span.spanData?.duration != null) {\n recorder.recordSpan({ name: span.name, durationMs: span.spanData.duration })\n }\n }\n return span\n }\n }\n if (prop === \"logger\") {\n // Child loggers inherit the metrics wrapper\n return (namespace?: string, childProps?: Record<string, unknown>): Logger => {\n const child = target.logger(namespace, childProps)\n // Re-wrap the child — withMetrics(recorder) applied recursively\n return withMetrics(recorder)(child as unknown as ConditionalLogger) as unknown as Logger\n }\n }\n return (target as unknown as Record<string | symbol, unknown>)[prop]\n },\n })\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA6CA,SAAS,WAAW,QAAkB,GAAmB;AACvD,KAAI,OAAO,WAAW,EAAG,QAAO;AAEhC,QAAO,OADK,KAAK,IAAI,KAAK,MAAM,OAAO,SAAS,EAAE,EAAE,OAAO,SAAS,EAAE;;AAIxE,SAAS,aAAa,WAAgC;CACpD,MAAM,SAAS,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE;CACnD,MAAM,QAAQ,OAAO,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE;AACnD,QAAO;EACL,OAAO,OAAO;EACd,KAAK,OAAO,MAAM;EAClB,KAAK,OAAO,OAAO,SAAS,MAAM;EAClC,MAAM,OAAO,SAAS,IAAI,QAAQ,OAAO,SAAS;EAClD,KAAK,WAAW,QAAQ,GAAI;EAC5B,KAAK,WAAW,QAAQ,IAAK;EAC7B,KAAK,WAAW,QAAQ,IAAK;EAC7B;EACD;;AAgBH,SAAgB,uBAAuB,aAAa,KAAwB;CAC1E,MAAM,wBAAQ,IAAI,KAAuB;AAEzC,QAAO;EACL,WAAW,MAAwB;GACjC,IAAI,MAAM,MAAM,IAAI,KAAK,KAAK;AAC9B,OAAI,CAAC,KAAK;AACR,UAAM,EAAE;AACR,UAAM,IAAI,KAAK,MAAM,IAAI;;AAE3B,OAAI,KAAK,KAAK,WAAW;AAEzB,OAAI,IAAI,SAAS,WAAY,KAAI,OAAO;;EAG1C,MAAM,MAAqC;GACzC,MAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,OAAI,CAAC,OAAO,IAAI,WAAW,EAAG,QAAO,KAAA;AACrC,UAAO,aAAa,IAAI;;EAG1B,MAA8B;GAC5B,MAAM,yBAAS,IAAI,KAAwB;AAC3C,QAAK,MAAM,CAAC,MAAM,cAAc,MAC9B,KAAI,UAAU,SAAS,EAAG,QAAO,IAAI,MAAM,aAAa,UAAU,CAAC;AAErE,UAAO;;EAGT,UAAkB;GAChB,MAAM,UAAU,CAAC,GAAG,KAAK,KAAK,CAAC,SAAS,CAAC;AACzC,OAAI,QAAQ,WAAW,EAAG,QAAO;AAKjC,UAJc,QAAQ,KACnB,CAAC,MAAM,OACN,GAAG,KAAK,IAAI,EAAE,MAAM,eAAe,EAAE,KAAK,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,QAAQ,EAAE,CAAC,IACzI,CACY,KAAK,KAAK;;EAGzB,QAAc;AACZ,SAAM,OAAO;;EAEhB;;AAKH,MAAM,WAAW,wBAAwB;AAKzC,oBAAoB,SAAS;;AAG7B,SAAgB,YAAoC;AAClD,QAAO,SAAS,KAAK;;;AAIvB,SAAgB,cAAsB;AACpC,QAAO,SAAS,SAAS;;;AAI3B,SAAgB,iBAAuB;AACrC,UAAS,OAAO;;;;;;;;;;;;;;;;AAmBlB,SAAgB,YAAY,WAA4E;CACtG,MAAM,WAAW,aAAa;AAE9B,SAAQ,WAAiD;AAEvD,SAAO,IAAI,MAAM,QAAQ,EACvB,IAAI,QAAQ,MAAuB;AACjC,OAAI,SAAS,QAAQ;IACnB,MAAM,eAAe,OAAO;AAC5B,QAAI,CAAC,aAAc,QAAO,KAAA;AAC1B,YAAQ,WAAoB,UAAkC;KAC5D,MAAM,OAAO,aAAa,KAAK,QAAQ,WAAW,MAAM;KAExD,MAAM,kBAAmB,KAAqD,OAAO;AACnF,UAAqD,OAAO,iBAAiB;AAC7E,sBAAgB,KAAK,KAAK;AAE1B,UAAI,KAAK,UAAU,YAAY,KAC7B,UAAS,WAAW;OAAE,MAAM,KAAK;OAAM,YAAY,KAAK,SAAS;OAAU,CAAC;;AAGhF,YAAO;;;AAGX,OAAI,SAAS,SAEX,SAAQ,WAAoB,eAAiD;IAC3E,MAAM,QAAQ,OAAO,OAAO,WAAW,WAAW;AAElD,WAAO,YAAY,SAAS,CAAC,MAAsC;;AAGvE,UAAQ,OAAuD;KAElE,CAAC"}
@@ -1,65 +0,0 @@
1
- import { l as SpanData } from "./core-7D7sstHl.mjs";
2
-
3
- //#region src/tracing.d.ts
4
- /** Supported ID formats */
5
- type IdFormat = "simple" | "w3c";
6
- /**
7
- * Set the ID format for new spans and traces.
8
- * - "simple": sp_1, sp_2, tr_1, tr_2 (default, lightweight)
9
- * - "w3c": 32-char hex trace ID, 16-char hex span ID (W3C Trace Context compatible)
10
- */
11
- declare function setIdFormat(format: IdFormat): void;
12
- /** Get the current ID format */
13
- declare function getIdFormat(): IdFormat;
14
- /** Generate a span ID according to the current format */
15
- declare function generateSpanId(): string;
16
- /** Generate a trace ID according to the current format */
17
- declare function generateTraceId(): string;
18
- /** Reset ID counters (for testing) */
19
- declare function resetIdCounters(): void;
20
- /** Options for traceparent header formatting */
21
- interface TraceparentOptions {
22
- /** Whether this span is sampled. Defaults to true for backwards compatibility. */
23
- sampled?: boolean;
24
- }
25
- /**
26
- * Format a W3C traceparent header from span data.
27
- *
28
- * Format: `{version}-{trace-id}-{span-id}-{trace-flags}`
29
- * - version: "00" (current W3C spec version)
30
- * - trace-id: 32 hex chars (128 bits)
31
- * - span-id: 16 hex chars (64 bits)
32
- * - trace-flags: "01" (sampled) or "00" (not sampled)
33
- *
34
- * Works with both simple and W3C ID formats. Simple IDs are zero-padded to spec length.
35
- *
36
- * @param spanData - Span data with id and traceId
37
- * @param options - Optional settings (sampled flag). Defaults to sampled=true.
38
- * @returns W3C traceparent header string
39
- *
40
- * @example
41
- * ```typescript
42
- * const span = log.span("http-request")
43
- * const header = traceparent(span.spanData)
44
- * // → "00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01"
45
- * fetch(url, { headers: { traceparent: header } })
46
- * ```
47
- */
48
- declare function traceparent(spanData: SpanData, options?: TraceparentOptions): string;
49
- /**
50
- * Set the head-based sampling rate for new traces.
51
- * Applied at trace creation — all spans within a sampled trace are kept.
52
- *
53
- * @param rate - Sampling rate from 0.0 (sample nothing) to 1.0 (sample everything, default)
54
- */
55
- declare function setSampleRate(rate: number): void;
56
- /** Get the current sampling rate */
57
- declare function getSampleRate(): number;
58
- /**
59
- * Determine whether a new trace should be sampled.
60
- * Called at trace creation time (head-based sampling).
61
- */
62
- declare function shouldSample(): boolean;
63
- //#endregion
64
- export { getIdFormat as a, setIdFormat as c, traceparent as d, generateTraceId as i, setSampleRate as l, TraceparentOptions as n, getSampleRate as o, generateSpanId as r, resetIdCounters as s, IdFormat as t, shouldSample as u };
65
- //# sourceMappingURL=tracing-2kv3HZ07.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tracing-2kv3HZ07.d.mts","names":[],"sources":["../src/tracing.ts"],"mappings":";;;;KAYY,QAAA;;;;;AAcZ;iBALgB,WAAA,CAAY,MAAA,EAAQ,QAAA;;iBAKpB,WAAA,CAAA,GAAe,QAAA;;iBAiBf,cAAA,CAAA;;iBAQA,eAAA,CAAA;;iBAQA,eAAA,CAAA;AARhB;AAAA,UAgBiB,kBAAA;;EAEf,OAAA;AAAA;AAVF;;;;;AAQA;;;;;AA4BA;;;;;;;;;;AAkCA;;;AAtEA,iBAoCgB,WAAA,CAAY,QAAA,EAAU,QAAA,EAAU,OAAA,GAAU,kBAAA;;AA0C1D;;;;;iBARgB,aAAA,CAAc,IAAA;;iBAQd,aAAA,CAAA;;;;;iBAQA,YAAA,CAAA"}
@@ -1,2 +0,0 @@
1
- import { a as getIdFormat, c as setIdFormat, d as traceparent, i as generateTraceId, l as setSampleRate, n as TraceparentOptions, o as getSampleRate, r as generateSpanId, s as resetIdCounters, t as IdFormat, u as shouldSample } from "./tracing-2kv3HZ07.mjs";
2
- export { IdFormat, TraceparentOptions, generateSpanId, generateTraceId, getIdFormat, getSampleRate, resetIdCounters, setIdFormat, setSampleRate, shouldSample, traceparent };
package/dist/tracing.mjs DELETED
@@ -1,96 +0,0 @@
1
- //#region src/tracing.ts
2
- let currentIdFormat = "simple";
3
- /**
4
- * Set the ID format for new spans and traces.
5
- * - "simple": sp_1, sp_2, tr_1, tr_2 (default, lightweight)
6
- * - "w3c": 32-char hex trace ID, 16-char hex span ID (W3C Trace Context compatible)
7
- */
8
- function setIdFormat(format) {
9
- currentIdFormat = format;
10
- }
11
- /** Get the current ID format */
12
- function getIdFormat() {
13
- return currentIdFormat;
14
- }
15
- let simpleSpanCounter = 0;
16
- let simpleTraceCounter = 0;
17
- /** Generate a hex string of the given byte length using crypto.randomUUID */
18
- function randomHex(bytes) {
19
- return crypto.randomUUID().replace(/-/g, "").slice(0, bytes * 2);
20
- }
21
- /** Generate a span ID according to the current format */
22
- function generateSpanId() {
23
- if (currentIdFormat === "w3c") return randomHex(8);
24
- return `sp_${(++simpleSpanCounter).toString(36)}`;
25
- }
26
- /** Generate a trace ID according to the current format */
27
- function generateTraceId() {
28
- if (currentIdFormat === "w3c") return randomHex(16);
29
- return `tr_${(++simpleTraceCounter).toString(36)}`;
30
- }
31
- /** Reset ID counters (for testing) */
32
- function resetIdCounters() {
33
- simpleSpanCounter = 0;
34
- simpleTraceCounter = 0;
35
- }
36
- /**
37
- * Format a W3C traceparent header from span data.
38
- *
39
- * Format: `{version}-{trace-id}-{span-id}-{trace-flags}`
40
- * - version: "00" (current W3C spec version)
41
- * - trace-id: 32 hex chars (128 bits)
42
- * - span-id: 16 hex chars (64 bits)
43
- * - trace-flags: "01" (sampled) or "00" (not sampled)
44
- *
45
- * Works with both simple and W3C ID formats. Simple IDs are zero-padded to spec length.
46
- *
47
- * @param spanData - Span data with id and traceId
48
- * @param options - Optional settings (sampled flag). Defaults to sampled=true.
49
- * @returns W3C traceparent header string
50
- *
51
- * @example
52
- * ```typescript
53
- * const span = log.span("http-request")
54
- * const header = traceparent(span.spanData)
55
- * // → "00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01"
56
- * fetch(url, { headers: { traceparent: header } })
57
- * ```
58
- */
59
- function traceparent(spanData, options) {
60
- return `00-${padHex(spanData.traceId, 32)}-${padHex(spanData.id, 16)}-${options?.sampled ?? true ? "01" : "00"}`;
61
- }
62
- /** Pad or hash an ID to the specified hex length */
63
- function padHex(id, length) {
64
- if (id.length === length && /^[0-9a-f]+$/.test(id)) return id;
65
- let hex = "";
66
- for (let i = 0; i < id.length; i++) hex += id.charCodeAt(i).toString(16).padStart(2, "0");
67
- return hex.padStart(length, "0").slice(-length);
68
- }
69
- let sampleRate = 1;
70
- /**
71
- * Set the head-based sampling rate for new traces.
72
- * Applied at trace creation — all spans within a sampled trace are kept.
73
- *
74
- * @param rate - Sampling rate from 0.0 (sample nothing) to 1.0 (sample everything, default)
75
- */
76
- function setSampleRate(rate) {
77
- if (rate < 0 || rate > 1) throw new Error(`Sample rate must be between 0.0 and 1.0, got ${rate}`);
78
- sampleRate = rate;
79
- }
80
- /** Get the current sampling rate */
81
- function getSampleRate() {
82
- return sampleRate;
83
- }
84
- /**
85
- * Determine whether a new trace should be sampled.
86
- * Called at trace creation time (head-based sampling).
87
- */
88
- function shouldSample() {
89
- if (sampleRate >= 1) return true;
90
- if (sampleRate <= 0) return false;
91
- return Math.random() < sampleRate;
92
- }
93
- //#endregion
94
- export { generateSpanId, generateTraceId, getIdFormat, getSampleRate, resetIdCounters, setIdFormat, setSampleRate, shouldSample, traceparent };
95
-
96
- //# sourceMappingURL=tracing.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tracing.mjs","names":[],"sources":["../src/tracing.ts"],"sourcesContent":["/**\n * Distributed tracing utilities for loggily.\n *\n * Provides W3C-compatible trace/span ID generation, traceparent header formatting,\n * and head-based sampling. All features are opt-in and don't break the existing API.\n */\n\nimport type { SpanData } from \"./core.js\"\n\n// ============ ID Format ============\n\n/** Supported ID formats */\nexport type IdFormat = \"simple\" | \"w3c\"\n\nlet currentIdFormat: IdFormat = \"simple\"\n\n/**\n * Set the ID format for new spans and traces.\n * - \"simple\": sp_1, sp_2, tr_1, tr_2 (default, lightweight)\n * - \"w3c\": 32-char hex trace ID, 16-char hex span ID (W3C Trace Context compatible)\n */\nexport function setIdFormat(format: IdFormat): void {\n currentIdFormat = format\n}\n\n/** Get the current ID format */\nexport function getIdFormat(): IdFormat {\n return currentIdFormat\n}\n\n// Simple format counters (used by core.ts via the generator functions)\nlet simpleSpanCounter = 0\nlet simpleTraceCounter = 0\n\n/** Generate a hex string of the given byte length using crypto.randomUUID */\nfunction randomHex(bytes: number): string {\n // crypto.randomUUID() gives us 32 hex chars (128 bits) after removing dashes\n // For 16 bytes (32 hex chars) we use one UUID, for 8 bytes (16 hex chars) we take a slice\n const uuid = crypto.randomUUID().replace(/-/g, \"\")\n return uuid.slice(0, bytes * 2)\n}\n\n/** Generate a span ID according to the current format */\nexport function generateSpanId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(8) // 16-char hex\n }\n return `sp_${(++simpleSpanCounter).toString(36)}`\n}\n\n/** Generate a trace ID according to the current format */\nexport function generateTraceId(): string {\n if (currentIdFormat === \"w3c\") {\n return randomHex(16) // 32-char hex\n }\n return `tr_${(++simpleTraceCounter).toString(36)}`\n}\n\n/** Reset ID counters (for testing) */\nexport function resetIdCounters(): void {\n simpleSpanCounter = 0\n simpleTraceCounter = 0\n}\n\n// ============ W3C Traceparent ============\n\n/** Options for traceparent header formatting */\nexport interface TraceparentOptions {\n /** Whether this span is sampled. Defaults to true for backwards compatibility. */\n sampled?: boolean\n}\n\n/**\n * Format a W3C traceparent header from span data.\n *\n * Format: `{version}-{trace-id}-{span-id}-{trace-flags}`\n * - version: \"00\" (current W3C spec version)\n * - trace-id: 32 hex chars (128 bits)\n * - span-id: 16 hex chars (64 bits)\n * - trace-flags: \"01\" (sampled) or \"00\" (not sampled)\n *\n * Works with both simple and W3C ID formats. Simple IDs are zero-padded to spec length.\n *\n * @param spanData - Span data with id and traceId\n * @param options - Optional settings (sampled flag). Defaults to sampled=true.\n * @returns W3C traceparent header string\n *\n * @example\n * ```typescript\n * const span = log.span(\"http-request\")\n * const header = traceparent(span.spanData)\n * // → \"00-a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6-1a2b3c4d5e6f7a8b-01\"\n * fetch(url, { headers: { traceparent: header } })\n * ```\n */\nexport function traceparent(spanData: SpanData, options?: TraceparentOptions): string {\n const traceId = padHex(spanData.traceId, 32)\n const spanId = padHex(spanData.id, 16)\n const flags = (options?.sampled ?? true) ? \"01\" : \"00\"\n return `00-${traceId}-${spanId}-${flags}`\n}\n\n/** Pad or hash an ID to the specified hex length */\nfunction padHex(id: string, length: number): string {\n // If it's already the right length and looks like hex, use as-is\n if (id.length === length && /^[0-9a-f]+$/.test(id)) {\n return id\n }\n\n // For simple IDs (sp_1, tr_1), create a deterministic hex representation\n // by encoding the string as hex bytes, zero-padded to the target length\n let hex = \"\"\n for (let i = 0; i < id.length; i++) {\n hex += id.charCodeAt(i).toString(16).padStart(2, \"0\")\n }\n // Pad or truncate to target length\n return hex.padStart(length, \"0\").slice(-length)\n}\n\n// ============ Sampling ============\n\nlet sampleRate = 1.0\n\n/**\n * Set the head-based sampling rate for new traces.\n * Applied at trace creation — all spans within a sampled trace are kept.\n *\n * @param rate - Sampling rate from 0.0 (sample nothing) to 1.0 (sample everything, default)\n */\nexport function setSampleRate(rate: number): void {\n if (rate < 0 || rate > 1) {\n throw new Error(`Sample rate must be between 0.0 and 1.0, got ${rate}`)\n }\n sampleRate = rate\n}\n\n/** Get the current sampling rate */\nexport function getSampleRate(): number {\n return sampleRate\n}\n\n/**\n * Determine whether a new trace should be sampled.\n * Called at trace creation time (head-based sampling).\n */\nexport function shouldSample(): boolean {\n if (sampleRate >= 1.0) return true\n if (sampleRate <= 0.0) return false\n return Math.random() < sampleRate\n}\n"],"mappings":";AAcA,IAAI,kBAA4B;;;;;;AAOhC,SAAgB,YAAY,QAAwB;AAClD,mBAAkB;;;AAIpB,SAAgB,cAAwB;AACtC,QAAO;;AAIT,IAAI,oBAAoB;AACxB,IAAI,qBAAqB;;AAGzB,SAAS,UAAU,OAAuB;AAIxC,QADa,OAAO,YAAY,CAAC,QAAQ,MAAM,GAAG,CACtC,MAAM,GAAG,QAAQ,EAAE;;;AAIjC,SAAgB,iBAAyB;AACvC,KAAI,oBAAoB,MACtB,QAAO,UAAU,EAAE;AAErB,QAAO,OAAO,EAAE,mBAAmB,SAAS,GAAG;;;AAIjD,SAAgB,kBAA0B;AACxC,KAAI,oBAAoB,MACtB,QAAO,UAAU,GAAG;AAEtB,QAAO,OAAO,EAAE,oBAAoB,SAAS,GAAG;;;AAIlD,SAAgB,kBAAwB;AACtC,qBAAoB;AACpB,sBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;AAkCvB,SAAgB,YAAY,UAAoB,SAAsC;AAIpF,QAAO,MAHS,OAAO,SAAS,SAAS,GAAG,CAGvB,GAFN,OAAO,SAAS,IAAI,GAAG,CAEP,GADhB,SAAS,WAAW,OAAQ,OAAO;;;AAKpD,SAAS,OAAO,IAAY,QAAwB;AAElD,KAAI,GAAG,WAAW,UAAU,cAAc,KAAK,GAAG,CAChD,QAAO;CAKT,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,QAAQ,IAC7B,QAAO,GAAG,WAAW,EAAE,CAAC,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI;AAGvD,QAAO,IAAI,SAAS,QAAQ,IAAI,CAAC,MAAM,CAAC,OAAO;;AAKjD,IAAI,aAAa;;;;;;;AAQjB,SAAgB,cAAc,MAAoB;AAChD,KAAI,OAAO,KAAK,OAAO,EACrB,OAAM,IAAI,MAAM,gDAAgD,OAAO;AAEzE,cAAa;;;AAIf,SAAgB,gBAAwB;AACtC,QAAO;;;;;;AAOT,SAAgB,eAAwB;AACtC,KAAI,cAAc,EAAK,QAAO;AAC9B,KAAI,cAAc,EAAK,QAAO;AAC9B,QAAO,KAAK,QAAQ,GAAG"}