@stacksjs/logging 0.72.8 → 0.72.9

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/index.d.ts CHANGED
@@ -1,3 +1,22 @@
1
+ import type { LogContext, LogLevel, LogRecord, LogTransport } from '@stacksjs/types';
2
+ // Request context propagation for structured logging, and the transport
3
+ // contract. Both are declared in `@stacksjs/types` so `config/logging.ts` can
4
+ // reference them without importing this package, which would be a cycle. They
5
+ // are re-exported here because this is where consumers already import them
6
+ // from.
7
+ export type { LogContext, LogLevel, LogRecord, LogTransport } from '@stacksjs/types';
8
+ /**
9
+ * Attach a transport at runtime, and get back a function that detaches it.
10
+ *
11
+ * The alternative to declaring one in `config/logging.ts`, for a package that
12
+ * has to attach without the application editing its config. Registering also
13
+ * initializes the logger, so a transport attached at boot starts receiving
14
+ * `debug` records immediately rather than waiting for the first console-visible
15
+ * line to build the config.
16
+ */
17
+ export declare function registerTransport(transport: LogTransport): () => void;
18
+ /** The transports currently attached. A copy, so callers cannot mutate the list. */
19
+ export declare function transports(): readonly LogTransport[];
1
20
  /**
2
21
  * Parse + validate `LOG_LEVEL` (stacksjs/stacks#1932). Previously the
3
22
  * env value was cast `as any` straight into the logger, so a typo
@@ -80,12 +99,6 @@ export declare function report(error: unknown, options?: ReportOptions): void;
80
99
  declare function emit(level: 'debug' | 'info' | 'warn' | 'error', event: string, fields: StructuredFields): void;
81
100
  export declare const log: Log;
82
101
  export declare const struct: unknown;
83
- // Request context propagation for structured logging
84
- export declare interface LogContext {
85
- requestId?: string
86
- userId?: string | number
87
- [key: string]: unknown
88
- }
89
102
  export declare interface ResolvedLogSettings {
90
103
  level: LogLevel
91
104
  format: LogFormat
@@ -160,8 +173,6 @@ export declare interface ReportOptions {
160
173
  * `log.struct` to a custom transport in tests.
161
174
  */
162
175
  declare interface StructuredFields { [key: string]: unknown }
163
- /** Valid log levels — mirrors `@stacksjs/clarity`'s `LogLevel`. */
164
- export type LogLevel = 'debug' | 'info' | 'success' | 'warning' | 'error';
165
176
  export type LogFormat = 'json' | 'text';
166
177
  export type ErrorMessage = string;
167
178
  // Export logger getter for debugging
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  // @bun
2
- var N=import.meta.require;import{AsyncLocalStorage as w}from"async_hooks";import X from"process";import{Logger as k}from"@stacksjs/clarity";import{handleError as E}from"@stacksjs/error-handling";import{ExitCode as T}from"@stacksjs/types";var j=null,P=null,D=new Set;function V(B){return D.add(B),B.finally(()=>D.delete(B)).catch(()=>{}),B}var O=!1;function C(){if(O)return;O=!0,X.on("beforeExit",()=>{G.flush()})}var M=new Set(["debug","info","success","warning","error"]);function b(B,H="info"){if(!B)return H;let J=B.toLowerCase();if(J==="warn")return"warning";if(M.has(J))return J;return X.stderr.write(`[logging] Ignoring invalid LOG_LEVEL="${B}" (expected: ${[...M].join(", ")}); using "${H}".
3
- `),H}function h(B){if(B==="json"||B==="text")return B;if(B)X.stderr.write(`[logging] Ignoring invalid LOG_FORMAT="${B}" (expected "json" or "text").
4
- `);return X.env.NODE_ENV==="production"?"json":"text"}function L(B){let H=B.envLevel?b(B.envLevel):B.cfgLevel?b(B.cfgLevel):"info",J=B.envFormat?h(B.envFormat):B.cfgFormat==="json"||B.cfgFormat==="text"?B.cfgFormat:B.isProduction?"json":"text";return{level:H,format:J,writeToFile:B.cfgWriteToFile??!0}}function q(B,H=0){if(H>8)return{name:"Error",message:"[cause chain truncated]"};if(B instanceof Error)return{name:B.name,message:B.message,stack:B.stack,cause:B.cause!=null?q(B.cause,H+1):void 0};if(typeof B==="string")return{name:"Error",message:B};if(B==null)return{name:"Error",message:String(B)};try{return{name:"Error",message:JSON.stringify(A(B))}}catch{return{name:"Error",message:String(B)}}}function S(B){let H=B.stack||`${B.name}: ${B.message}`,J=B.cause;while(J)H+=`
5
- caused by: ${J.stack||`${J.name}: ${J.message}`}`,J=J.cause;return H}function A(B,H=0){if(B instanceof Error)return q(B);if(H>=4||B===null||typeof B!=="object")return B;if(Array.isArray(B))return B.map((U)=>A(U,H+1));let J=Object.getPrototypeOf(B);if(J!==Object.prototype&&J!==null)return B;let Q={};for(let[U,Z]of Object.entries(B))Q[U]=A(Z,H+1);return Q}function _(B){return A(B)}var F=new w;function o(B,H){return F.run(B,H)}function y(){let B=F.getStore(),H=f();if(!H)return B;return{trace_id:H,...B}}function f(){try{let H=globalThis[Symbol.for("stacks.router.traceStorage")]?.getStore?.();if(H)return H;return globalThis[Symbol.for("stacks.router.requestStorage")]?.getStore?.()?._requestId}catch{return}}async function x(){if(j)return;if(P)return P;return C(),P=(async()=>{let B,H,J,Q;try{let K=(await import("@stacksjs/config")).logging;if(K){if(B=K.level,H=K.format,typeof K.writeToFile==="boolean")Q=K.writeToFile;if(K.logsPath)J=(await import("path")).dirname(K.logsPath)}}catch{}let{level:U,format:Z,writeToFile:I}=L({envLevel:X.env.LOG_LEVEL,envFormat:X.env.LOG_FORMAT,cfgLevel:B,cfgFormat:H,cfgWriteToFile:Q,isProduction:X.env.NODE_ENV==="production"}),W=J;if(!W)try{W=(await import("@stacksjs/path")).projectPath("storage/logs")}catch{W="storage/logs"}j=new k("stacks",{level:U,logDirectory:W,showTags:!1,fancy:Z!=="json",format:Z,writeToFile:I})})(),P}async function $(){return await x(),j}function Y(...B){let H=B.filter((Q)=>Q!==void 0).map((Q)=>{if(Q instanceof Error)return S(q(Q));if(typeof Q==="object"&&Q!==null)return JSON.stringify(A(Q),null,2);return String(Q)}).join(" "),J=F.getStore();if(J?.requestId)return`[${J.requestId}] ${H}`;return H}var v=new Set(["shouldExit","silent","message"]);function d(B){if(!B||typeof B!=="object"||B instanceof Error)return!1;if(!(("shouldExit"in B)||("silent"in B)))return!1;return Object.keys(B).every((H)=>v.has(H))}var G={info:async(...B)=>{let H=Y(...B);await(await $()).info(H)},success:async(B)=>{await(await $()).success(B)},warn:async(B,H)=>{let J=await $();if(H==null){await J.warn(B);return}let Q=A(H);await J.warn(B,Q)},warning:async(B)=>{await(await $()).warn(B)},error:async(B,H,J)=>{let Q=d(H)?H:void 0,U;if(typeof B==="string")U=B;else U=S(q(B));if(H!==void 0&&!Q)U=`${U} ${S(q(H))}`;let Z={...y(),...J};if(Object.keys(Z).length>0)try{U=`${U} ${JSON.stringify(_(Z))}`}catch{}if(await(await $()).error(U),Q?.shouldExit)E(B,Q)},debug:async(...B)=>{let H=(X.env.LOG_LEVEL||"info").toLowerCase();if(H==="info"||H==="warn"||H==="error")return;let J=Y(...B);await(await $()).debug(J)},dump:async(...B)=>{let H=Y(...B);await(await $()).debug(`DUMP: ${H}`)},dd:async(...B)=>{let H=Y(...B);await(await $()).error(H),X.exit(T.FatalError)},echo:async(...B)=>{let H=Y(...B);await(await $()).info(`ECHO: ${H}`)},time:(B)=>{let H=performance.now();return async(J)=>{let Q=performance.now()-H,U=await $(),Z=J?` ${JSON.stringify(_(J))}`:"";await U.info(`${B}: ${Q.toFixed(2)}ms${Z}`)}},syncWarn:(B)=>{X.stderr.write(`${B}
2
+ var R=import.meta.require;import{AsyncLocalStorage as x}from"async_hooks";import X from"process";import{Logger as v}from"@stacksjs/clarity";import{handleError as u}from"@stacksjs/error-handling";import{ExitCode as S}from"@stacksjs/types";var I=null,F=null,T=new Set;function z(B){return T.add(B),B.finally(()=>T.delete(B)).catch(()=>{}),B}var w=!1;function d(){if(w)return;w=!0,X.on("beforeExit",()=>{G.flush()})}var K=[],y=new Set,k={debug:0,info:1,success:1,warning:2,error:3};function h(B){let H=B;if(!H||typeof H!=="object"||typeof H.log!=="function")return X.stderr.write(`[logging] Ignoring a transport without a log() function.
3
+ `),null;if(typeof H.name!=="string"||!H.name)return X.stderr.write(`[logging] Ignoring a transport without a name.
4
+ `),null;return K.push(H),()=>{let J=K.indexOf(H);if(J!==-1)K.splice(J,1)}}function HB(B){let H=h(B);if(!H)return()=>{};return L(),H}function JB(){return[...K]}function q(B,H,J){if(K.length===0)return;let Q;try{Q={level:B,message:H,args:J,context:V(),timestamp:new Date().toISOString()}}catch{return}for(let U of K){if(U.level&&k[B]<k[U.level])continue;try{U.log(Q)}catch(Z){if(!y.has(U.name)){y.add(U.name);let j=Z instanceof Error?Z.message:String(Z);X.stderr.write(`[logging] Transport "${U.name}" threw: ${j}. Further throws from it are silent.
5
+ `)}}}}async function c(){let B=K.filter((H)=>typeof H.flush==="function").map((H)=>H.flush().catch(()=>{}));if(B.length>0)await Promise.allSettled(B)}var C=new Set(["debug","info","success","warning","error"]);function _(B,H="info"){if(!B)return H;let J=B.toLowerCase();if(J==="warn")return"warning";if(C.has(J))return J;return X.stderr.write(`[logging] Ignoring invalid LOG_LEVEL="${B}" (expected: ${[...C].join(", ")}); using "${H}".
6
+ `),H}function m(B){if(B==="json"||B==="text")return B;if(B)X.stderr.write(`[logging] Ignoring invalid LOG_FORMAT="${B}" (expected "json" or "text").
7
+ `);return X.env.NODE_ENV==="production"?"json":"text"}function p(B){let H=B.envLevel?_(B.envLevel):B.cfgLevel?_(B.cfgLevel):"info",J=B.envFormat?m(B.envFormat):B.cfgFormat==="json"||B.cfgFormat==="text"?B.cfgFormat:B.isProduction?"json":"text";return{level:H,format:J,writeToFile:B.cfgWriteToFile??!0}}function P(B,H=0){if(H>8)return{name:"Error",message:"[cause chain truncated]"};if(B instanceof Error)return{name:B.name,message:B.message,stack:B.stack,cause:B.cause!=null?P(B.cause,H+1):void 0};if(typeof B==="string")return{name:"Error",message:B};if(B==null)return{name:"Error",message:String(B)};try{return{name:"Error",message:JSON.stringify(W(B))}}catch{return{name:"Error",message:String(B)}}}function M(B){let H=B.stack||`${B.name}: ${B.message}`,J=B.cause;while(J)H+=`
8
+ caused by: ${J.stack||`${J.name}: ${J.message}`}`,J=J.cause;return H}function W(B,H=0){if(B instanceof Error)return P(B);if(H>=4||B===null||typeof B!=="object")return B;if(Array.isArray(B))return B.map((U)=>W(U,H+1));let J=Object.getPrototypeOf(B);if(J!==Object.prototype&&J!==null)return B;let Q={};for(let[U,Z]of Object.entries(B))Q[U]=W(Z,H+1);return Q}function E(B){return W(B)}var O=new x;function QB(B,H){return O.run(B,H)}function V(){let B=O.getStore(),H=i();if(!H)return B;return{trace_id:H,...B}}function i(){try{let H=globalThis[Symbol.for("stacks.router.traceStorage")]?.getStore?.();if(H)return H;return globalThis[Symbol.for("stacks.router.requestStorage")]?.getStore?.()?._requestId}catch{return}}async function L(){if(I)return;if(F)return F;return d(),F=(async()=>{let B,H,J,Q;try{let Y=(await import("@stacksjs/config")).logging;if(Y){if(B=Y.level,H=Y.format,typeof Y.writeToFile==="boolean")Q=Y.writeToFile;if(Y.logsPath)J=(await import("path")).dirname(Y.logsPath);if(Array.isArray(Y.transports))for(let b of Y.transports)h(b)}}catch{}let{level:U,format:Z,writeToFile:j}=p({envLevel:X.env.LOG_LEVEL,envFormat:X.env.LOG_FORMAT,cfgLevel:B,cfgFormat:H,cfgWriteToFile:Q,isProduction:X.env.NODE_ENV==="production"}),A=J;if(!A)try{A=(await import("@stacksjs/path")).projectPath("storage/logs")}catch{A="storage/logs"}I=new v("stacks",{level:U,logDirectory:A,showTags:!1,fancy:Z!=="json",format:Z,writeToFile:j})})(),F}async function $(){return await L(),I}function N(...B){let H=B.filter((Q)=>Q!==void 0).map((Q)=>{if(Q instanceof Error)return M(P(Q));if(typeof Q==="object"&&Q!==null)return JSON.stringify(W(Q),null,2);return String(Q)}).join(" "),J=O.getStore();if(J?.requestId)return`[${J.requestId}] ${H}`;return H}var n=new Set(["shouldExit","silent","message"]);function l(B){if(!B||typeof B!=="object"||B instanceof Error)return!1;if(!(("shouldExit"in B)||("silent"in B)))return!1;return Object.keys(B).every((H)=>n.has(H))}var G={info:async(...B)=>{let H=N(...B),J=await $();q("info",H,B),await J.info(H)},success:async(B)=>{let H=await $();q("success",B,[B]),await H.success(B)},warn:async(B,H)=>{let J=await $();if(q("warning",B,H===void 0?[B]:[B,H]),H==null){await J.warn(B);return}let Q=W(H);await J.warn(B,Q)},warning:async(B)=>{let H=await $();q("warning",B,[B]),await H.warn(B)},error:async(B,H,J)=>{let Q=l(H)?H:void 0,U;if(typeof B==="string")U=B;else U=M(P(B));if(H!==void 0&&!Q)U=`${U} ${M(P(H))}`;let Z={...V(),...J};if(Object.keys(Z).length>0)try{U=`${U} ${JSON.stringify(E(Z))}`}catch{}let j=await $();if(q("error",U,Q?[B]:[B,H,J].filter((A)=>A!==void 0)),await j.error(U),Q?.shouldExit)u(B,Q)},debug:async(...B)=>{let H=(X.env.LOG_LEVEL||"info").toLowerCase(),J=H==="info"||H==="warn"||H==="error";if(J&&K.length===0)return;let Q=N(...B);if(J){q("debug",Q,B);return}let U=await $();q("debug",Q,B),await U.debug(Q)},dump:async(...B)=>{let H=N(...B);await(await $()).debug(`DUMP: ${H}`)},dd:async(...B)=>{let H=N(...B);await(await $()).error(H),X.exit(S.FatalError)},echo:async(...B)=>{let H=N(...B);await(await $()).info(`ECHO: ${H}`)},time:(B)=>{let H=performance.now();return async(J)=>{let Q=performance.now()-H,U=await $(),Z=J?` ${JSON.stringify(E(J))}`:"";await U.info(`${B}: ${Q.toFixed(2)}ms${Z}`)}},syncWarn:(B)=>{X.stderr.write(`${B}
6
9
  `)},syncError:(B)=>{X.stderr.write(`${B}
7
- `)},fatal:(B,H=T.FatalError)=>{X.stderr.write(`${B}
8
- `),X.exit(H)},exit:async(B,H=T.Success)=>{if(B)await(H===T.Success?G.success(B):G.error(B));await G.flush(),X.exit(H)},flush:async()=>{if(D.size>0)await Promise.allSettled([...D]);if(!j&&!P)return;try{let B=await $(),H=B.flush;if(typeof H==="function")await H.call(B)}catch{}}};async function s(...B){for(let H of B)await G.debug(H)}async function t(...B){let H=Y(...B);console.log(H),X.exit(T.FatalError)}async function r(...B){await G.debug(...B)}function c(B){if(B&&typeof B==="object"){let H=B;if(typeof H.status==="number")return H.status;if(typeof H.statusCode==="number")return H.statusCode}return}function e(B,H={}){let J=H.status??c(B),Q=typeof J==="number"&&J>=400&&J<500,U=H.label??"Unhandled error",Z={...H.context,...J!=null?{status:J}:{}};if(Q){G.debug(`${U} (client error ${J}): ${q(B).message}`);return}G.error(U,B,Z)}function R(B,H,J){let Q=y(),U={event:H,traceId:Q?.requestId,...J};if(B==="warn")V(G.warn(`[${H}]`,U));else V(G[B](U))}var u={request(B,H,J,Q,U={}){let Z=J>=500?"error":J>=400?"warn":"info";R(Z,"http.request",{method:B,path:H,status:J,durationMs:Q,...U})},query(B,H,J={}){R("debug","db.query",{sql:B,durationMs:H,...J})},slowQuery(B,H,J={}){R("warn","db.slow_query",{sql:B,durationMs:H,...J})},job(B,H,J={}){R(H==="failed"?"error":"info",`job.${H}`,{jobName:B,...J})},cache(B,H,J={}){R("debug",`cache.${B}`,{key:H,...J})}};G.struct=u;export{o as withLogContext,u as struct,L as resolveLogSettings,e as report,S as renderNormalizedError,b as parseLogLevel,h as parseLogFormat,q as normalizeError,_ as normalizeContext,$ as logger,G as log,y as getLogContext,Y as formatMessage,r as echo,s as dump,t as dd};
10
+ `)},fatal:(B,H=S.FatalError)=>{X.stderr.write(`${B}
11
+ `),X.exit(H)},exit:async(B,H=S.Success)=>{if(B)await(H===S.Success?G.success(B):G.error(B));await G.flush(),X.exit(H)},flush:async()=>{if(T.size>0)await Promise.allSettled([...T]);if(await c(),!I&&!F)return;try{let B=await $(),H=B.flush;if(typeof H==="function")await H.call(B)}catch{}}};async function UB(...B){for(let H of B)await G.debug(H)}async function XB(...B){let H=N(...B);console.log(H),X.exit(S.FatalError)}async function ZB(...B){await G.debug(...B)}function g(B){if(B&&typeof B==="object"){let H=B;if(typeof H.status==="number")return H.status;if(typeof H.statusCode==="number")return H.statusCode}return}function $B(B,H={}){let J=H.status??g(B),Q=typeof J==="number"&&J>=400&&J<500,U=H.label??"Unhandled error",Z={...H.context,...J!=null?{status:J}:{}};if(Q){G.debug(`${U} (client error ${J}): ${P(B).message}`);return}G.error(U,B,Z)}function D(B,H,J){let Q=V(),U={event:H,traceId:Q?.requestId,...J};if(B==="warn")z(G.warn(`[${H}]`,U));else z(G[B](U))}var o={request(B,H,J,Q,U={}){let Z=J>=500?"error":J>=400?"warn":"info";D(Z,"http.request",{method:B,path:H,status:J,durationMs:Q,...U})},query(B,H,J={}){D("debug","db.query",{sql:B,durationMs:H,...J})},slowQuery(B,H,J={}){D("warn","db.slow_query",{sql:B,durationMs:H,...J})},job(B,H,J={}){D(H==="failed"?"error":"info",`job.${H}`,{jobName:B,...J})},cache(B,H,J={}){D("debug",`cache.${B}`,{key:H,...J})}};G.struct=o;export{QB as withLogContext,JB as transports,o as struct,p as resolveLogSettings,$B as report,M as renderNormalizedError,HB as registerTransport,_ as parseLogLevel,m as parseLogFormat,P as normalizeError,E as normalizeContext,$ as logger,G as log,V as getLogContext,N as formatMessage,ZB as echo,UB as dump,XB as dd};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/logging",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.72.8",
5
+ "version": "0.72.9",
6
6
  "description": "The Stacks logging system.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -61,12 +61,12 @@
61
61
  "@stacksjs/clarity": "^0.3.31"
62
62
  },
63
63
  "devDependencies": {
64
- "@stacksjs/cli": "0.72.8",
65
- "@stacksjs/config": "0.72.8",
66
- "@stacksjs/error-handling": "0.72.8",
67
- "@stacksjs/path": "0.72.8",
68
- "@stacksjs/storage": "0.72.8",
69
- "@stacksjs/validation": "0.72.8",
64
+ "@stacksjs/cli": "0.72.9",
65
+ "@stacksjs/config": "0.72.9",
66
+ "@stacksjs/error-handling": "0.72.9",
67
+ "@stacksjs/path": "0.72.9",
68
+ "@stacksjs/storage": "0.72.9",
69
+ "@stacksjs/validation": "0.72.9",
70
70
  "better-dx": "^0.2.23"
71
71
  }
72
72
  }