jsonl-logger 0.3.0 → 0.4.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/README.md CHANGED
@@ -83,6 +83,58 @@ console.log('plain text') // → structured JSON
83
83
  originalConsole.log('bypass interception')
84
84
  ```
85
85
 
86
+ ## OpenTelemetry
87
+
88
+ The logger supports automatic trace context injection. Supply a `traceContext` getter that returns the active span's trace/span IDs — the formatter maps them to platform-specific fields automatically.
89
+
90
+ ### With `@opentelemetry/api`
91
+
92
+ ```typescript
93
+ import { trace } from '@opentelemetry/api'
94
+ import { Logger } from 'jsonl-logger'
95
+
96
+ const logger = new Logger({}, {
97
+ traceContext: () => {
98
+ const span = trace.getActiveSpan()
99
+ if (!span) return undefined
100
+ const { traceId, spanId, traceFlags } = span.spanContext()
101
+ return { traceId, spanId, traceFlags }
102
+ },
103
+ })
104
+
105
+ logger.info('request handled', { path: '/api' })
106
+ // GCL output includes "logging.googleapis.com/trace", "logging.googleapis.com/spanId", etc.
107
+ // VictoriaLogs output includes "trace_id", "span_id", etc.
108
+ ```
109
+
110
+ ### Custom trace context
111
+
112
+ ```typescript
113
+ const logger = new Logger({}, {
114
+ traceContext: () => ({
115
+ traceId: myTracer.currentTraceId(),
116
+ spanId: myTracer.currentSpanId(),
117
+ }),
118
+ })
119
+ ```
120
+
121
+ The `traceContext` option is also available on `intercept()`:
122
+
123
+ ```typescript
124
+ import { intercept } from 'jsonl-logger/intercept'
125
+
126
+ intercept({
127
+ traceContext: () => {
128
+ const span = trace.getActiveSpan()
129
+ if (!span) return undefined
130
+ const { traceId, spanId, traceFlags } = span.spanContext()
131
+ return { traceId, spanId, traceFlags }
132
+ },
133
+ })
134
+ ```
135
+
136
+ Child loggers inherit the `traceContext` getter from their parent.
137
+
86
138
  ## Next.js Integration
87
139
 
88
140
  The preload module reads `LOG_FORMAT` and only activates when it's set. Safe to include unconditionally — it's a no-op without `LOG_FORMAT`.
@@ -188,7 +240,7 @@ The logger auto-detects the runtime and uses the fastest available I/O:
188
240
 
189
241
  | Subpath | Export |
190
242
  |---------|--------|
191
- | `jsonl-logger` | `Logger`, `logger`, `errorInfo()`, types (`ErrorInfo`, `LogRecord`, etc.) |
243
+ | `jsonl-logger` | `Logger`, `logger`, `errorInfo()`, types (`ErrorInfo`, `LogRecord`, `TraceContext`, etc.) |
192
244
  | `jsonl-logger/google-cloud-logging` | `GoogleCloudLogging` formatter |
193
245
  | `jsonl-logger/victoria-logs` | `VictoriaLogs` formatter |
194
246
  | `jsonl-logger/intercept` | `intercept()`, `originalConsole` |
@@ -1 +1 @@
1
- var t={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},r={messageKey:"message",format(e){return{message:e.message,timestamp:e.timestamp,severity:t[e.level],...e.context}}};export{r as GoogleCloudLogging};
1
+ var g={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},a={messageKey:"message",format(e){let t={message:e.message,timestamp:e.timestamp,severity:g[e.level],...e.context};if(e.trace){if(t["logging.googleapis.com/trace"]=e.trace.traceId,t["logging.googleapis.com/spanId"]=e.trace.spanId,e.trace.traceFlags!==void 0)t["logging.googleapis.com/trace_sampled"]=(e.trace.traceFlags&1)===1}return t}};export{a as GoogleCloudLogging};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ErrorInfo, LogContext, LoggerOptions } from './types';
2
- export type { ErrorInfo, Formatter, FormatterName, InterceptOptions, LogContext, LoggerOptions, LogLevel, LogRecord, } from './types';
2
+ export type { ErrorInfo, Formatter, FormatterName, InterceptOptions, LogContext, LoggerOptions, LogLevel, LogRecord, TraceContext, } from './types';
3
3
  export { logLevelValues, stripAnsi } from './types';
4
4
  export declare function errorInfo(err: Error): ErrorInfo;
5
5
  export declare class Logger {
@@ -7,6 +7,7 @@ export declare class Logger {
7
7
  private min;
8
8
  private json;
9
9
  private fmt;
10
+ private tc?;
10
11
  constructor(context?: LogContext, options?: LoggerOptions);
11
12
  child(context: LogContext): Logger;
12
13
  private log;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- var F={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},L={messageKey:"message",format(o){return{message:o.message,timestamp:o.timestamp,severity:F[o.level],...o.context}}};var d={messageKey:"_msg",format(o){return{_msg:o.message,_time:o.timestamp,level:o.level,...o.context}}};var i=process.env.LOG_FORMAT,R=!!i,r={debug:0,info:1,warn:2,error:3,fatal:4},I=/\x1b\[[0-9;]*m/g;function u(o){return o.replace(I,"")}function v(o,t,e="error"){if(o[`${e}.name`]=t.name,o[`${e}.message`]=t.message,t.stack)o[`${e}.stack`]=t.stack;if(t.cause)v(o,t.cause,`${e}.cause`)}var f=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",x=f==="deno"?new TextEncoder:null;function w(o,t){if(f==="node")(t?process.stderr??process.stdout:process.stdout).write(`${o}
2
- `);else if(f==="deno"&&x){let e=x.encode(`${o}
3
- `);if(t)Deno.stderr.writeSync(e);else Deno.stdout.writeSync(e)}else if(t)console.error(o);else console.log(o)}var y={"google-cloud-logging":L,"victoria-logs":d},E=i&&y[i]||L,b=R,O=process.env.LOG_LEVEL||(b?"info":"debug"),S={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function C(o,t){return t.add(o),{name:o.name,message:o.message,stack:o.stack,...o.cause instanceof Error&&!t.has(o.cause)?{cause:C(o.cause,t)}:{}}}function j(o){return C(o,new WeakSet)}class p{ctx;min;json;fmt;constructor(o,t){this.ctx=o||{},this.json=t?.json??b,this.fmt=t?.formatter??E;let e=t?.level??O;this.min=r[e]??r.info}child(o){let t=new p({...this.ctx,...o},{json:this.json,formatter:this.fmt});return t.min=this.min,t}log(o,t,e,m){if(r[o]<this.min)return;let s={level:o,message:this.json?u(t).trim():t,timestamp:new Date().toISOString(),context:e?{...this.ctx,...e}:this.ctx};if(m)s.error=j(m);if(this.json){let g=this.fmt.format(s);if(s.error)v(g,s.error);w(JSON.stringify(g),S[o])}else this.logPlain(o,s)}logPlain(o,t){let e={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},m="\x1B[0m",s=e[o],g=new Date(t.timestamp).toLocaleTimeString("en-US",{hour12:!1}),k=o.toUpperCase().padEnd(5),h=t.context,$=Object.keys(h).length>0?` ${JSON.stringify(h)}`:"",c="";if(t.error){let n=t.error,l=!0;while(n){if(n.stack)c+=l?`
1
+ var k={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},f={messageKey:"message",format(t){let o={message:t.message,timestamp:t.timestamp,severity:k[t.level],...t.context};if(t.trace){if(o["logging.googleapis.com/trace"]=t.trace.traceId,o["logging.googleapis.com/spanId"]=t.trace.spanId,t.trace.traceFlags!==void 0)o["logging.googleapis.com/trace_sampled"]=(t.trace.traceFlags&1)===1}return o}};var d={messageKey:"_msg",format(t){let o={_msg:t.message,_time:t.timestamp,level:t.level,...t.context};if(t.trace){if(o.trace_id=t.trace.traceId,o.span_id=t.trace.spanId,t.trace.traceFlags!==void 0)o.trace_flags=t.trace.traceFlags}return o}};var i=process.env.LOG_FORMAT,C=!!i,a={debug:0,info:1,warn:2,error:3,fatal:4},$=/\x1b\[[0-9;]*m/g;function u(t){return t.replace($,"")}function p(t,o,e="error"){if(t[`${e}.name`]=o.name,t[`${e}.message`]=o.message,o.stack)t[`${e}.stack`]=o.stack;if(o.cause)p(t,o.cause,`${e}.cause`)}var L=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",x=L==="deno"?new TextEncoder:null;function R(t,o){if(L==="node")(o?process.stderr??process.stdout:process.stdout).write(`${t}
2
+ `);else if(L==="deno"&&x){let e=x.encode(`${t}
3
+ `);if(o)Deno.stderr.writeSync(e);else Deno.stdout.writeSync(e)}else if(o)console.error(t);else console.log(t)}var E={"google-cloud-logging":f,"victoria-logs":d},O=i&&E[i]||f,w=C,S=process.env.LOG_LEVEL||(w?"info":"debug"),y={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function b(t,o){return o.add(t),{name:t.name,message:t.message,stack:t.stack,...t.cause instanceof Error&&!o.has(t.cause)?{cause:b(t.cause,o)}:{}}}function j(t){return b(t,new WeakSet)}class h{ctx;min;json;fmt;tc;constructor(t,o){this.ctx=t||{},this.json=o?.json??w,this.fmt=o?.formatter??O,this.tc=o?.traceContext;let e=o?.level??S;this.min=a[e]??a.info}child(t){let o=new h({...this.ctx,...t},{json:this.json,formatter:this.fmt,traceContext:this.tc});return o.min=this.min,o}log(t,o,e,c){if(a[t]<this.min)return;let s={level:t,message:this.json?u(o).trim():o,timestamp:new Date().toISOString(),context:e?{...this.ctx,...e}:this.ctx};if(this.tc)s.trace=this.tc();if(c)s.error=j(c);if(this.json){let g=this.fmt.format(s);if(s.error)p(g,s.error);R(JSON.stringify(g),y[t])}else this.logPlain(t,s)}logPlain(t,o){let e={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},c="\x1B[0m",s=e[t],g=new Date(o.timestamp).toLocaleTimeString("en-US",{hour12:!1}),I=t.toUpperCase().padEnd(5),v=o.context,F=Object.keys(v).length>0?` ${JSON.stringify(v)}`:"",m="";if(o.error){let n=o.error,l=!0;while(n){if(n.stack)m+=l?`
4
4
  ${n.stack}`:`
5
- Caused by: ${n.stack}`;else c+=l?`
5
+ Caused by: ${n.stack}`;else m+=l?`
6
6
  ${n.name}: ${n.message}`:`
7
- Caused by: ${n.name}: ${n.message}`;n=n.cause,l=!1}}let a=`${s}${g} ${k}\x1B[0m ${t.message}${$}${c}`;switch(o){case"debug":console.debug(a);break;case"warn":console.warn(a);break;case"error":case"fatal":console.error(a);break;default:console.log(a)}}debug(o,t){this.log("debug",o,t)}info(o,t){this.log("info",o,t)}warn(o,t){this.log("warn",o,t)}error(o,t,e){this.log("error",o,t,e)}fatal(o,t,e){this.log("fatal",o,t,e)}}var U=new p;export{u as stripAnsi,U as logger,r as logLevelValues,j as errorInfo,p as Logger};
7
+ Caused by: ${n.name}: ${n.message}`;n=n.cause,l=!1}}let r=`${s}${g} ${I}\x1B[0m ${o.message}${F}${m}`;switch(t){case"debug":console.debug(r);break;case"warn":console.warn(r);break;case"error":case"fatal":console.error(r);break;default:console.log(r)}}debug(t,o){this.log("debug",t,o)}info(t,o){this.log("info",t,o)}warn(t,o){this.log("warn",t,o)}error(t,o,e){this.log("error",t,o,e)}fatal(t,o,e){this.log("fatal",t,o,e)}}var J=new h;export{u as stripAnsi,J as logger,a as logLevelValues,j as errorInfo,h as Logger};
package/dist/intercept.js CHANGED
@@ -1,7 +1,7 @@
1
- var d={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},a={messageKey:"message",format(o){return{message:o.message,timestamp:o.timestamp,severity:d[o.level],...o.context}}};var l={messageKey:"_msg",format(o){return{_msg:o.message,_time:o.timestamp,level:o.level,...o.context}}};var b=process.env.LOG_FORMAT,I=!!b,m={debug:0,info:1,warn:2,error:3,fatal:4},E=/\x1b\[[0-9;]*m/g;function u(o){return o.replace(E,"")}function h(o,t,n="error"){if(o[`${n}.name`]=t.name,o[`${n}.message`]=t.message,t.stack)o[`${n}.stack`]=t.stack;if(t.cause)h(o,t.cause,`${n}.cause`)}var w=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",F=w==="deno"?new TextEncoder:null;function R(o,t){if(w==="node")(t?process.stderr??process.stdout:process.stdout).write(`${o}
2
- `);else if(w==="deno"&&F){let n=F.encode(`${o}
3
- `);if(t)Deno.stderr.writeSync(n);else Deno.stdout.writeSync(n)}else if(t)console.error(o);else console.log(o)}var N={"google-cloud-logging":a,"victoria-logs":l},y=b&&N[b]||a,v=I,G=process.env.LOG_LEVEL||(v?"info":"debug"),_={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function O(o,t){return t.add(o),{name:o.name,message:o.message,stack:o.stack,...o.cause instanceof Error&&!t.has(o.cause)?{cause:O(o.cause,t)}:{}}}function k(o){return O(o,new WeakSet)}class ${ctx;min;json;fmt;constructor(o,t){this.ctx=o||{},this.json=t?.json??v,this.fmt=t?.formatter??y;let n=t?.level??G;this.min=m[n]??m.info}child(o){let t=new $({...this.ctx,...o},{json:this.json,formatter:this.fmt});return t.min=this.min,t}log(o,t,n,s){if(m[o]<this.min)return;let c={level:o,message:this.json?u(t).trim():t,timestamp:new Date().toISOString(),context:n?{...this.ctx,...n}:this.ctx};if(s)c.error=k(s);if(this.json){let e=this.fmt.format(c);if(c.error)h(e,c.error);R(JSON.stringify(e),_[o])}else this.logPlain(o,c)}logPlain(o,t){let n={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},s="\x1B[0m",c=n[o],e=new Date(t.timestamp).toLocaleTimeString("en-US",{hour12:!1}),f=o.toUpperCase().padEnd(5),x=t.context,p=Object.keys(x).length>0?` ${JSON.stringify(x)}`:"",L="";if(t.error){let i=t.error,C=!0;while(i){if(i.stack)L+=C?`
4
- ${i.stack}`:`
5
- Caused by: ${i.stack}`;else L+=C?`
6
- ${i.name}: ${i.message}`:`
7
- Caused by: ${i.name}: ${i.message}`;i=i.cause,C=!1}}let g=`${c}${e} ${f}\x1B[0m ${t.message}${p}${L}`;switch(o){case"debug":console.debug(g);break;case"warn":console.warn(g);break;case"error":case"fatal":console.error(g);break;default:console.log(g)}}debug(o,t){this.log("debug",o,t)}info(o,t){this.log("info",o,t)}warn(o,t){this.log("warn",o,t)}error(o,t,n){this.log("error",o,t,n)}fatal(o,t,n){this.log("fatal",o,t,n)}}var P=new $;var K={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console),debug:console.debug.bind(console)};function A(...o){let t="";for(let n=0;n<o.length;n++){if(n>0)t+=" ";let s=o[n];if(typeof s==="string")t+=u(s);else if(s instanceof Error)t+=s.message;else try{t+=JSON.stringify(s)}catch{t+=String(s)}}return t.trim()}function J(...o){let t;for(let n of o)if(typeof n==="object"&&n!==null&&!(n instanceof Error)&&!Array.isArray(n)){if(!t)t={};Object.assign(t,n)}return t}function U(...o){for(let t of o)if(t instanceof Error)return t}var S={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function V(o,t,n,s){let c=`"${t.messageKey}"`;return(...e)=>{if(e.length===1&&typeof e[0]==="string"&&e[0].charCodeAt(0)===123&&e[0].includes(c)){R(e[0],S[o]);return}if(m[o]<n)return;let f=A(...e);if(s&&!s(o,f))return;let x=J(...e),p=U(...e),L={level:o,message:f,timestamp:new Date().toISOString(),context:x||{},error:p?k(p):void 0},g=t.format(L);if(L.error)h(g,L.error);R(JSON.stringify(g),S[o])}}var j="__jsonlLoggerIntercepted";function M(o){if(globalThis[j])return;globalThis[j]=!0;let t=o?.formatter??a,n=m[o?.level??"debug"],s=o?.filter,c=[["log","info"],["info","info"],["warn","warn"],["error","error"],["debug","debug"]];for(let[e,f]of c)console[e]=V(f,t,n,s)}export{K as originalConsole,M as intercept};
1
+ var j={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},u={messageKey:"message",format(t){let o={message:t.message,timestamp:t.timestamp,severity:j[t.level],...t.context};if(t.trace){if(o["logging.googleapis.com/trace"]=t.trace.traceId,o["logging.googleapis.com/spanId"]=t.trace.spanId,t.trace.traceFlags!==void 0)o["logging.googleapis.com/trace_sampled"]=(t.trace.traceFlags&1)===1}return o}};var w={messageKey:"_msg",format(t){let o={_msg:t.message,_time:t.timestamp,level:t.level,...t.context};if(t.trace){if(o.trace_id=t.trace.traceId,o.span_id=t.trace.spanId,t.trace.traceFlags!==void 0)o.trace_flags=t.trace.traceFlags}return o}};var x=process.env.LOG_FORMAT,$=!!x,m={debug:0,info:1,warn:2,error:3,fatal:4},_=/\x1b\[[0-9;]*m/g;function h(t){return t.replace(_,"")}function p(t,o,n="error"){if(t[`${n}.name`]=o.name,t[`${n}.message`]=o.message,o.stack)t[`${n}.stack`]=o.stack;if(o.cause)p(t,o.cause,`${n}.cause`)}var b=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",k=b==="deno"?new TextEncoder:null;function l(t,o){if(b==="node")(o?process.stderr??process.stdout:process.stdout).write(`${t}
2
+ `);else if(b==="deno"&&k){let n=k.encode(`${t}
3
+ `);if(o)Deno.stderr.writeSync(n);else Deno.stdout.writeSync(n)}else if(o)console.error(t);else console.log(t)}var E={"google-cloud-logging":u,"victoria-logs":w},N=x&&E[x]||u,v=$,T=process.env.LOG_LEVEL||(v?"info":"debug"),y={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function d(t,o){return o.add(t),{name:t.name,message:t.message,stack:t.stack,...t.cause instanceof Error&&!o.has(t.cause)?{cause:d(t.cause,o)}:{}}}function F(t){return d(t,new WeakSet)}class I{ctx;min;json;fmt;tc;constructor(t,o){this.ctx=t||{},this.json=o?.json??v,this.fmt=o?.formatter??N,this.tc=o?.traceContext;let n=o?.level??T;this.min=m[n]??m.info}child(t){let o=new I({...this.ctx,...t},{json:this.json,formatter:this.fmt,traceContext:this.tc});return o.min=this.min,o}log(t,o,n,e){if(m[t]<this.min)return;let c={level:t,message:this.json?h(o).trim():o,timestamp:new Date().toISOString(),context:n?{...this.ctx,...n}:this.ctx};if(this.tc)c.trace=this.tc();if(e)c.error=F(e);if(this.json){let a=this.fmt.format(c);if(c.error)p(a,c.error);l(JSON.stringify(a),y[t])}else this.logPlain(t,c)}logPlain(t,o){let n={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},e="\x1B[0m",c=n[t],a=new Date(o.timestamp).toLocaleTimeString("en-US",{hour12:!1}),i=t.toUpperCase().padEnd(5),L=o.context,R=Object.keys(L).length>0?` ${JSON.stringify(L)}`:"",f="";if(o.error){let s=o.error,C=!0;while(s){if(s.stack)f+=C?`
4
+ ${s.stack}`:`
5
+ Caused by: ${s.stack}`;else f+=C?`
6
+ ${s.name}: ${s.message}`:`
7
+ Caused by: ${s.name}: ${s.message}`;s=s.cause,C=!1}}let g=`${c}${a} ${i}\x1B[0m ${o.message}${R}${f}`;switch(t){case"debug":console.debug(g);break;case"warn":console.warn(g);break;case"error":case"fatal":console.error(g);break;default:console.log(g)}}debug(t,o){this.log("debug",t,o)}info(t,o){this.log("info",t,o)}warn(t,o){this.log("warn",t,o)}error(t,o,n){this.log("error",t,o,n)}fatal(t,o,n){this.log("fatal",t,o,n)}}var H=new I;var D={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console),debug:console.debug.bind(console)};function G(...t){let o="";for(let n=0;n<t.length;n++){if(n>0)o+=" ";let e=t[n];if(typeof e==="string")o+=h(e);else if(e instanceof Error)o+=e.message;else try{o+=JSON.stringify(e)}catch{o+=String(e)}}return o.trim()}function A(...t){let o;for(let n of t)if(typeof n==="object"&&n!==null&&!(n instanceof Error)&&!Array.isArray(n)){if(!o)o={};Object.assign(o,n)}return o}function J(...t){for(let o of t)if(o instanceof Error)return o}var O={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function U(t,o,n,e,c){let a=`"${o.messageKey}"`;return(...i)=>{if(i.length===1&&typeof i[0]==="string"&&i[0].charCodeAt(0)===123&&i[0].includes(a)){l(i[0],O[t]);return}if(m[t]<n)return;let L=G(...i);if(e&&!e(t,L))return;let R=A(...i),f=J(...i),g={level:t,message:L,timestamp:new Date().toISOString(),context:R||{},error:f?F(f):void 0,trace:c?.()},s=o.format(g);if(g.error)p(s,g.error);l(JSON.stringify(s),O[t])}}var S="__jsonlLoggerIntercepted";function K(t){if(globalThis[S])return;globalThis[S]=!0;let o=t?.formatter??u,n=m[t?.level??"debug"],e=t?.filter,c=t?.traceContext,a=[["log","info"],["info","info"],["warn","warn"],["error","error"],["debug","debug"]];for(let[i,L]of a)console[i]=U(L,o,n,e,c)}export{D as originalConsole,K as intercept};
package/dist/preload.js CHANGED
@@ -1,7 +1,7 @@
1
- var N={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},c={messageKey:"message",format(o){return{message:o.message,timestamp:o.timestamp,severity:N[o.level],...o.context}}};var x={messageKey:"_msg",format(o){return{_msg:o.message,_time:o.timestamp,level:o.level,...o.context}}};var a=process.env.LOG_FORMAT,v=!!a,L={debug:0,info:1,warn:2,error:3,fatal:4},S=/\x1b\[[0-9;]*m/g;function u(o){return o.replace(S,"")}function p(o,t,n="error"){if(o[`${n}.name`]=t.name,o[`${n}.message`]=t.message,t.stack)o[`${n}.stack`]=t.stack;if(t.cause)p(o,t.cause,`${n}.cause`)}var C=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",k=C==="deno"?new TextEncoder:null;function l(o,t){if(C==="node")(t?process.stderr??process.stdout:process.stdout).write(`${o}
2
- `);else if(C==="deno"&&k){let n=k.encode(`${o}
3
- `);if(t)Deno.stderr.writeSync(n);else Deno.stdout.writeSync(n)}else if(t)console.error(o);else console.log(o)}var j={"google-cloud-logging":c,"victoria-logs":x},y=a&&j[a]||c,$=v,G=process.env.LOG_LEVEL||($?"info":"debug"),V={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function d(o,t){return t.add(o),{name:o.name,message:o.message,stack:o.stack,...o.cause instanceof Error&&!t.has(o.cause)?{cause:d(o.cause,t)}:{}}}function F(o){return d(o,new WeakSet)}class w{ctx;min;json;fmt;constructor(o,t){this.ctx=o||{},this.json=t?.json??$,this.fmt=t?.formatter??y;let n=t?.level??G;this.min=L[n]??L.info}child(o){let t=new w({...this.ctx,...o},{json:this.json,formatter:this.fmt});return t.min=this.min,t}log(o,t,n,e){if(L[o]<this.min)return;let i={level:o,message:this.json?u(t).trim():t,timestamp:new Date().toISOString(),context:n?{...this.ctx,...n}:this.ctx};if(e)i.error=F(e);if(this.json){let s=this.fmt.format(i);if(i.error)p(s,i.error);l(JSON.stringify(s),V[o])}else this.logPlain(o,i)}logPlain(o,t){let n={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},e="\x1B[0m",i=n[o],s=new Date(t.timestamp).toLocaleTimeString("en-US",{hour12:!1}),r=o.toUpperCase().padEnd(5),R=t.context,h=Object.keys(R).length>0?` ${JSON.stringify(R)}`:"",g="";if(t.error){let m=t.error,b=!0;while(m){if(m.stack)g+=b?`
4
- ${m.stack}`:`
5
- Caused by: ${m.stack}`;else g+=b?`
6
- ${m.name}: ${m.message}`:`
7
- Caused by: ${m.name}: ${m.message}`;m=m.cause,b=!1}}let f=`${i}${s} ${r}\x1B[0m ${t.message}${h}${g}`;switch(o){case"debug":console.debug(f);break;case"warn":console.warn(f);break;case"error":case"fatal":console.error(f);break;default:console.log(f)}}debug(o,t){this.log("debug",o,t)}info(o,t){this.log("info",o,t)}warn(o,t){this.log("warn",o,t)}error(o,t,n){this.log("error",o,t,n)}fatal(o,t,n){this.log("fatal",o,t,n)}}var P=new w;var K={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console),debug:console.debug.bind(console)};function _(...o){let t="";for(let n=0;n<o.length;n++){if(n>0)t+=" ";let e=o[n];if(typeof e==="string")t+=u(e);else if(e instanceof Error)t+=e.message;else try{t+=JSON.stringify(e)}catch{t+=String(e)}}return t.trim()}function A(...o){let t;for(let n of o)if(typeof n==="object"&&n!==null&&!(n instanceof Error)&&!Array.isArray(n)){if(!t)t={};Object.assign(t,n)}return t}function J(...o){for(let t of o)if(t instanceof Error)return t}var I={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function U(o,t,n,e){let i=`"${t.messageKey}"`;return(...s)=>{if(s.length===1&&typeof s[0]==="string"&&s[0].charCodeAt(0)===123&&s[0].includes(i)){l(s[0],I[o]);return}if(L[o]<n)return;let r=_(...s);if(e&&!e(o,r))return;let R=A(...s),h=J(...s),g={level:o,message:r,timestamp:new Date().toISOString(),context:R||{},error:h?F(h):void 0},f=t.format(g);if(g.error)p(f,g.error);l(JSON.stringify(f),I[o])}}var O="__jsonlLoggerIntercepted";function E(o){if(globalThis[O])return;globalThis[O]=!0;let t=o?.formatter??c,n=L[o?.level??"debug"],e=o?.filter,i=[["log","info"],["info","info"],["warn","warn"],["error","error"],["debug","debug"]];for(let[s,r]of i)console[s]=U(r,t,n,e)}if(a){let t={"google-cloud-logging":c,"victoria-logs":x}[a]??c,n=process.env.LOG_LEVEL||"info";E({formatter:t,level:n})}
1
+ var N={debug:"DEBUG",info:"INFO",warn:"WARNING",error:"ERROR",fatal:"CRITICAL"},g={messageKey:"message",format(t){let o={message:t.message,timestamp:t.timestamp,severity:N[t.level],...t.context};if(t.trace){if(o["logging.googleapis.com/trace"]=t.trace.traceId,o["logging.googleapis.com/spanId"]=t.trace.spanId,t.trace.traceFlags!==void 0)o["logging.googleapis.com/trace_sampled"]=(t.trace.traceFlags&1)===1}return o}};var x={messageKey:"_msg",format(t){let o={_msg:t.message,_time:t.timestamp,level:t.level,...t.context};if(t.trace){if(o.trace_id=t.trace.traceId,o.span_id=t.trace.spanId,t.trace.traceFlags!==void 0)o.trace_flags=t.trace.traceFlags}return o}};var l=process.env.LOG_FORMAT,d=!!l,f={debug:0,info:1,warn:2,error:3,fatal:4},S=/\x1b\[[0-9;]*m/g;function r(t){return t.replace(S,"")}function u(t,o,n="error"){if(t[`${n}.name`]=o.name,t[`${n}.message`]=o.message,o.stack)t[`${n}.stack`]=o.stack;if(o.cause)u(t,o.cause,`${n}.cause`)}var C=typeof process<"u"&&process.stdout&&typeof process.stdout.write==="function"?"node":typeof Deno<"u"&&Deno.stdout?"deno":"browser",I=C==="deno"?new TextEncoder:null;function h(t,o){if(C==="node")(o?process.stderr??process.stdout:process.stdout).write(`${t}
2
+ `);else if(C==="deno"&&I){let n=I.encode(`${t}
3
+ `);if(o)Deno.stderr.writeSync(n);else Deno.stdout.writeSync(n)}else if(o)console.error(t);else console.log(t)}var _={"google-cloud-logging":g,"victoria-logs":x},j=l&&_[l]||g,v=d,y=process.env.LOG_LEVEL||(v?"info":"debug"),G={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function k(t,o){return o.add(t),{name:t.name,message:t.message,stack:t.stack,...t.cause instanceof Error&&!o.has(t.cause)?{cause:k(t.cause,o)}:{}}}function b(t){return k(t,new WeakSet)}class w{ctx;min;json;fmt;tc;constructor(t,o){this.ctx=t||{},this.json=o?.json??v,this.fmt=o?.formatter??j,this.tc=o?.traceContext;let n=o?.level??y;this.min=f[n]??f.info}child(t){let o=new w({...this.ctx,...t},{json:this.json,formatter:this.fmt,traceContext:this.tc});return o.min=this.min,o}log(t,o,n,e){if(f[t]<this.min)return;let i={level:t,message:this.json?r(o).trim():o,timestamp:new Date().toISOString(),context:n?{...this.ctx,...n}:this.ctx};if(this.tc)i.trace=this.tc();if(e)i.error=b(e);if(this.json){let a=this.fmt.format(i);if(i.error)u(a,i.error);h(JSON.stringify(a),G[t])}else this.logPlain(t,i)}logPlain(t,o){let n={debug:"\x1B[36m",info:"\x1B[32m",warn:"\x1B[33m",error:"\x1B[31m",fatal:"\x1B[35m"},e="\x1B[0m",i=n[t],a=new Date(o.timestamp).toLocaleTimeString("en-US",{hour12:!1}),c=t.toUpperCase().padEnd(5),L=o.context,R=Object.keys(L).length>0?` ${JSON.stringify(L)}`:"",p="";if(o.error){let s=o.error,F=!0;while(s){if(s.stack)p+=F?`
4
+ ${s.stack}`:`
5
+ Caused by: ${s.stack}`;else p+=F?`
6
+ ${s.name}: ${s.message}`:`
7
+ Caused by: ${s.name}: ${s.message}`;s=s.cause,F=!1}}let m=`${i}${a} ${c}\x1B[0m ${o.message}${R}${p}`;switch(t){case"debug":console.debug(m);break;case"warn":console.warn(m);break;case"error":case"fatal":console.error(m);break;default:console.log(m)}}debug(t,o){this.log("debug",t,o)}info(t,o){this.log("info",t,o)}warn(t,o){this.log("warn",t,o)}error(t,o,n){this.log("error",t,o,n)}fatal(t,o,n){this.log("fatal",t,o,n)}}var P=new w;var K={log:console.log.bind(console),info:console.info.bind(console),warn:console.warn.bind(console),error:console.error.bind(console),debug:console.debug.bind(console)};function T(...t){let o="";for(let n=0;n<t.length;n++){if(n>0)o+=" ";let e=t[n];if(typeof e==="string")o+=r(e);else if(e instanceof Error)o+=e.message;else try{o+=JSON.stringify(e)}catch{o+=String(e)}}return o.trim()}function V(...t){let o;for(let n of t)if(typeof n==="object"&&n!==null&&!(n instanceof Error)&&!Array.isArray(n)){if(!o)o={};Object.assign(o,n)}return o}function A(...t){for(let o of t)if(o instanceof Error)return o}var $={debug:!1,info:!1,warn:!1,error:!0,fatal:!0};function J(t,o,n,e,i){let a=`"${o.messageKey}"`;return(...c)=>{if(c.length===1&&typeof c[0]==="string"&&c[0].charCodeAt(0)===123&&c[0].includes(a)){h(c[0],$[t]);return}if(f[t]<n)return;let L=T(...c);if(e&&!e(t,L))return;let R=V(...c),p=A(...c),m={level:t,message:L,timestamp:new Date().toISOString(),context:R||{},error:p?b(p):void 0,trace:i?.()},s=o.format(m);if(m.error)u(s,m.error);h(JSON.stringify(s),$[t])}}var O="__jsonlLoggerIntercepted";function E(t){if(globalThis[O])return;globalThis[O]=!0;let o=t?.formatter??g,n=f[t?.level??"debug"],e=t?.filter,i=t?.traceContext,a=[["log","info"],["info","info"],["warn","warn"],["error","error"],["debug","debug"]];for(let[c,L]of a)console[c]=J(L,o,n,e,i)}if(l){let o={"google-cloud-logging":g,"victoria-logs":x}[l]??g,n=process.env.LOG_LEVEL||"info";E({formatter:o,level:n})}
package/dist/types.d.ts CHANGED
@@ -6,12 +6,18 @@ export type ErrorInfo = {
6
6
  stack?: string;
7
7
  cause?: ErrorInfo;
8
8
  };
9
+ export type TraceContext = {
10
+ traceId: string;
11
+ spanId: string;
12
+ traceFlags?: number;
13
+ };
9
14
  export type LogRecord = {
10
15
  level: LogLevel;
11
16
  message: string;
12
17
  timestamp: string;
13
18
  context: LogContext;
14
19
  error?: ErrorInfo;
20
+ trace?: TraceContext;
15
21
  };
16
22
  export type Formatter = {
17
23
  format: (record: LogRecord) => Record<string, unknown>;
@@ -21,6 +27,7 @@ export type LoggerOptions = {
21
27
  formatter?: Formatter;
22
28
  json?: boolean;
23
29
  level?: LogLevel;
30
+ traceContext?: () => TraceContext | undefined;
24
31
  };
25
32
  export type FormatterName = 'google-cloud-logging' | 'victoria-logs';
26
33
  export declare const defaultFormat: FormatterName | undefined;
@@ -29,6 +36,7 @@ export type InterceptOptions = {
29
36
  formatter?: Formatter;
30
37
  filter?: (level: LogLevel, message: string) => boolean;
31
38
  level?: LogLevel;
39
+ traceContext?: () => TraceContext | undefined;
32
40
  };
33
41
  export declare const logLevelValues: Record<LogLevel, number>;
34
42
  export declare function stripAnsi(str: string): string;
@@ -1 +1 @@
1
- var o={messageKey:"_msg",format(t){return{_msg:t.message,_time:t.timestamp,level:t.level,...t.context}}};export{o as VictoriaLogs};
1
+ var a={messageKey:"_msg",format(t){let e={_msg:t.message,_time:t.timestamp,level:t.level,...t.context};if(t.trace){if(e.trace_id=t.trace.traceId,e.span_id=t.trace.spanId,t.trace.traceFlags!==void 0)e.trace_flags=t.trace.traceFlags}return e}};export{a as VictoriaLogs};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsonl-logger",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Lightweight ESM-only JSON Lines logger with pluggable formatters for Google Cloud Logging, VictoriaLogs, and more",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -49,7 +49,7 @@
49
49
  "test": "bun test"
50
50
  },
51
51
  "devDependencies": {
52
- "@biomejs/biome": "2.4.2",
52
+ "@biomejs/biome": "2.4.3",
53
53
  "@types/bun": "1.3.9",
54
54
  "typescript": "5.9.3"
55
55
  },
@@ -17,6 +17,14 @@ export const GoogleCloudLogging: Formatter = {
17
17
  severity: severityMap[record.level],
18
18
  ...record.context,
19
19
  }
20
+ if (record.trace) {
21
+ entry['logging.googleapis.com/trace'] = record.trace.traceId
22
+ entry['logging.googleapis.com/spanId'] = record.trace.spanId
23
+ if (record.trace.traceFlags !== undefined) {
24
+ entry['logging.googleapis.com/trace_sampled'] =
25
+ (record.trace.traceFlags & 1) === 1
26
+ }
27
+ }
20
28
  return entry
21
29
  },
22
30
  }
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ import type {
7
7
  LoggerOptions,
8
8
  LogLevel,
9
9
  LogRecord,
10
+ TraceContext,
10
11
  } from './types'
11
12
  import {
12
13
  defaultFormat,
@@ -27,6 +28,7 @@ export type {
27
28
  LoggerOptions,
28
29
  LogLevel,
29
30
  LogRecord,
31
+ TraceContext,
30
32
  } from './types'
31
33
  export { logLevelValues, stripAnsi } from './types'
32
34
 
@@ -71,11 +73,13 @@ export class Logger {
71
73
  private min: number
72
74
  private json: boolean
73
75
  private fmt: Formatter
76
+ private tc?: () => TraceContext | undefined
74
77
 
75
78
  constructor(context?: LogContext, options?: LoggerOptions) {
76
79
  this.ctx = context || {}
77
80
  this.json = options?.json ?? defaultJson
78
81
  this.fmt = options?.formatter ?? defaultFormatter
82
+ this.tc = options?.traceContext
79
83
  const level: LogLevel = options?.level ?? defaultLevel
80
84
  this.min = logLevelValues[level] ?? logLevelValues.info
81
85
  }
@@ -86,6 +90,7 @@ export class Logger {
86
90
  {
87
91
  json: this.json,
88
92
  formatter: this.fmt,
93
+ traceContext: this.tc,
89
94
  },
90
95
  )
91
96
  child.min = this.min
@@ -107,6 +112,10 @@ export class Logger {
107
112
  context: meta ? { ...this.ctx, ...meta } : this.ctx,
108
113
  }
109
114
 
115
+ if (this.tc) {
116
+ record.trace = this.tc()
117
+ }
118
+
110
119
  if (err) {
111
120
  record.error = errorInfo(err)
112
121
  }
@@ -145,7 +154,9 @@ export class Logger {
145
154
  let isRoot = true
146
155
  while (current) {
147
156
  if (current.stack) {
148
- errStr += isRoot ? `\n${current.stack}` : `\nCaused by: ${current.stack}`
157
+ errStr += isRoot
158
+ ? `\n${current.stack}`
159
+ : `\nCaused by: ${current.stack}`
149
160
  } else {
150
161
  errStr += isRoot
151
162
  ? `\n ${current.name}: ${current.message}`
package/src/intercept.ts CHANGED
@@ -1,6 +1,11 @@
1
1
  import { GoogleCloudLogging } from './google-cloud-logging'
2
2
  import { errorInfo } from './index'
3
- import type { Formatter, InterceptOptions, LogLevel } from './types'
3
+ import type {
4
+ Formatter,
5
+ InterceptOptions,
6
+ LogLevel,
7
+ TraceContext,
8
+ } from './types'
4
9
  import { flattenError, logLevelValues, stripAnsi, write } from './types'
5
10
 
6
11
  type ConsoleMethods = {
@@ -75,6 +80,7 @@ function createOverride(
75
80
  formatter: Formatter,
76
81
  minLevel: number,
77
82
  filter?: (level: LogLevel, message: string) => boolean,
83
+ traceContext?: () => TraceContext | undefined,
78
84
  ): (...args: unknown[]) => void {
79
85
  const msgKey = `"${formatter.messageKey}"`
80
86
 
@@ -105,6 +111,7 @@ function createOverride(
105
111
  timestamp: new Date().toISOString(),
106
112
  context: meta || {},
107
113
  error: error ? errorInfo(error) : undefined,
114
+ trace: traceContext?.(),
108
115
  }
109
116
 
110
117
  const formatted = formatter.format(record)
@@ -122,6 +129,7 @@ export function intercept(options?: InterceptOptions): void {
122
129
  const formatter = options?.formatter ?? GoogleCloudLogging
123
130
  const minLevel = logLevelValues[options?.level ?? 'debug']
124
131
  const filter = options?.filter
132
+ const traceContext = options?.traceContext
125
133
 
126
134
  const methodMap: [keyof ConsoleMethods, LogLevel][] = [
127
135
  ['log', 'info'],
@@ -132,6 +140,12 @@ export function intercept(options?: InterceptOptions): void {
132
140
  ]
133
141
 
134
142
  for (const [method, level] of methodMap) {
135
- console[method] = createOverride(level, formatter, minLevel, filter)
143
+ console[method] = createOverride(
144
+ level,
145
+ formatter,
146
+ minLevel,
147
+ filter,
148
+ traceContext,
149
+ )
136
150
  }
137
151
  }
package/src/types.ts CHANGED
@@ -9,12 +9,19 @@ export type ErrorInfo = {
9
9
  cause?: ErrorInfo
10
10
  }
11
11
 
12
+ export type TraceContext = {
13
+ traceId: string
14
+ spanId: string
15
+ traceFlags?: number
16
+ }
17
+
12
18
  export type LogRecord = {
13
19
  level: LogLevel
14
20
  message: string
15
21
  timestamp: string
16
22
  context: LogContext
17
23
  error?: ErrorInfo
24
+ trace?: TraceContext
18
25
  }
19
26
 
20
27
  export type Formatter = {
@@ -26,6 +33,7 @@ export type LoggerOptions = {
26
33
  formatter?: Formatter
27
34
  json?: boolean
28
35
  level?: LogLevel
36
+ traceContext?: () => TraceContext | undefined
29
37
  }
30
38
 
31
39
  export type FormatterName = 'google-cloud-logging' | 'victoria-logs'
@@ -37,6 +45,7 @@ export type InterceptOptions = {
37
45
  formatter?: Formatter
38
46
  filter?: (level: LogLevel, message: string) => boolean
39
47
  level?: LogLevel
48
+ traceContext?: () => TraceContext | undefined
40
49
  }
41
50
 
42
51
  export const logLevelValues: Record<LogLevel, number> = {
@@ -9,6 +9,13 @@ export const VictoriaLogs: Formatter = {
9
9
  level: record.level,
10
10
  ...record.context,
11
11
  }
12
+ if (record.trace) {
13
+ entry.trace_id = record.trace.traceId
14
+ entry.span_id = record.trace.spanId
15
+ if (record.trace.traceFlags !== undefined) {
16
+ entry.trace_flags = record.trace.traceFlags
17
+ }
18
+ }
12
19
  return entry
13
20
  },
14
21
  }