@stacksjs/logging 0.70.72 → 0.70.74

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
@@ -59,6 +59,7 @@ export declare function echo(...args: any[]): Promise<void>;
59
59
  * (stacksjs/stacks#1934) drains it.
60
60
  */
61
61
  export declare function report(error: unknown, options?: ReportOptions): void;
62
+ declare function emit(level: 'debug' | 'info' | 'warn' | 'error', event: string, fields: StructuredFields): void;
62
63
  export declare const log: Log;
63
64
  export declare const struct: unknown;
64
65
  // Request context propagation for structured logging
@@ -89,7 +90,7 @@ export declare interface Log {
89
90
  info: (...args: unknown[]) => Promise<void>
90
91
  success: (msg: string) => Promise<void>
91
92
  error: (message: string | Error | unknown, error?: unknown, context?: LogContext) => Promise<void>
92
- warn: (arg: string, context?: LogContext) => Promise<void>
93
+ warn: (arg: string, context?: unknown) => Promise<void>
93
94
  warning: (arg: string) => Promise<void>
94
95
  debug: (...args: unknown[]) => Promise<void>
95
96
  dump: (...args: unknown[]) => Promise<void>
@@ -119,6 +120,26 @@ export declare interface ReportOptions {
119
120
  context?: LogContext
120
121
  label?: string
121
122
  }
123
+ /**
124
+ * Structured logging shorthands for common framework events.
125
+ *
126
+ * The bare `log.info("…")` form is good for ad-hoc messages, but the
127
+ * framework emits a predictable set of events (HTTP requests, DB
128
+ * queries, queued jobs, cache operations) that benefit from a stable
129
+ * shape so downstream log shippers can index on consistent field
130
+ * names.
131
+ *
132
+ * Each helper:
133
+ * 1. Attaches the current trace id (if any) automatically
134
+ * 2. Picks the appropriate severity based on outcome
135
+ * 3. Emits a consistent JSON shape in production (`event`, `level`,
136
+ * `traceId`, …) while keeping the human-readable form in dev
137
+ *
138
+ * Helpers are batched onto `log.struct` so they don't pollute the
139
+ * top-level `log` namespace, and so users can opt out by routing
140
+ * `log.struct` to a custom transport in tests.
141
+ */
142
+ declare interface StructuredFields { [key: string]: unknown }
122
143
  /** Valid log levels — mirrors `@stacksjs/clarity`'s `LogLevel`. */
123
144
  export type LogLevel = 'debug' | 'info' | 'success' | 'warning' | 'error';
124
145
  export type LogFormat = 'json' | 'text';
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // @bun
2
- var A=import.meta.require;import{AsyncLocalStorage as w}from"async_hooks";import X from"process";import{Logger as k}from"@stacksjs/clarity";import{handleError as C}from"@stacksjs/error-handling";import{ExitCode as I}from"@stacksjs/types";var W=null,T=null,j=new Set;function O(B){return j.add(B),B.finally(()=>j.delete(B)).catch(()=>{}),B}var M=!1;function E(){if(M)return;M=!0,X.on("beforeExit",()=>{G.flush()})}var b=new Set(["debug","info","success","warning","error"]);function z(B,H="info"){if(!B)return H;let J=B.toLowerCase();if(J==="warn")return"warning";if(b.has(J))return J;return X.stderr.write(`[logging] Ignoring invalid LOG_LEVEL="${B}" (expected: ${[...b].join(", ")}); using "${H}".
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 C}from"@stacksjs/error-handling";import{ExitCode as D}from"@stacksjs/types";var W=null,R=null,j=new Set;function V(B){return j.add(B),B.finally(()=>j.delete(B)).catch(()=>{}),B}var O=!1;function E(){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
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?z(B.envLevel):B.cfgLevel?z(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(R(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 R(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)=>R(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]=R(Z,H+1);return Q}function D(B){return R(B)}var F=new w;function o(B,H){return F.run(B,H)}function _(){return F.getStore()}async function x(){if(W)return;if(T)return T;return E(),T=(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:V}=L({envLevel:X.env.LOG_LEVEL,envFormat:X.env.LOG_FORMAT,cfgLevel:B,cfgFormat:H,cfgWriteToFile:Q,isProduction:X.env.NODE_ENV==="production"}),P=J;if(!P)try{P=(await import("@stacksjs/path")).projectPath("storage/logs")}catch{P="storage/logs"}W=new k("stacks",{level:U,logDirectory:P,showTags:!1,fancy:Z!=="json",format:Z,writeToFile:V})})(),T}async function $(){return await x(),W}function Y(...B){let H=B.map((Q)=>{if(Q instanceof Error)return S(q(Q));if(typeof Q==="object"&&Q!==null)return JSON.stringify(R(Q),null,2);return String(Q)}).join(" "),J=F.getStore();if(J?.requestId)return`[${J.requestId}] ${H}`;return H}var f=new Set(["shouldExit","silent","message"]);function v(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)=>f.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)=>{await(await $()).warn(B,H?D(H):void 0)},warning:async(B)=>{await(await $()).warn(B)},error:async(B,H,J)=>{let Q=v(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={..._(),...J};if(Object.keys(Z).length>0)try{U=`${U} ${JSON.stringify(D(Z))}`}catch{}if(await(await $()).error(U),Q?.shouldExit)C(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(I.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(D(J))}`:"";await U.info(`${B}: ${Q.toFixed(2)}ms${Z}`)}},syncWarn:(B)=>{X.stderr.write(`${B}
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 I(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 S=new w;function o(B,H){return S.run(B,H)}function y(){return S.getStore()}async function x(){if(W)return;if(R)return R;return E(),R=(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:F}=L({envLevel:X.env.LOG_LEVEL,envFormat:X.env.LOG_FORMAT,cfgLevel:B,cfgFormat:H,cfgWriteToFile:Q,isProduction:X.env.NODE_ENV==="production"}),P=J;if(!P)try{P=(await import("@stacksjs/path")).projectPath("storage/logs")}catch{P="storage/logs"}W=new k("stacks",{level:U,logDirectory:P,showTags:!1,fancy:Z!=="json",format:Z,writeToFile:F})})(),R}async function $(){return await x(),W}function Y(...B){let H=B.map((Q)=>{if(Q instanceof Error)return I(q(Q));if(typeof Q==="object"&&Q!==null)return JSON.stringify(A(Q),null,2);return String(Q)}).join(" "),J=S.getStore();if(J?.requestId)return`[${J.requestId}] ${H}`;return H}var f=new Set(["shouldExit","silent","message"]);function v(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)=>f.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 $(),Q=H===void 0?void 0:A(H);await J.warn(B,Q)},warning:async(B)=>{await(await $()).warn(B)},error:async(B,H,J)=>{let Q=v(H)?H:void 0,U;if(typeof B==="string")U=B;else U=I(q(B));if(H!==void 0&&!Q)U=`${U} ${I(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)C(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(D.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}
6
6
  `)},syncError:(B)=>{X.stderr.write(`${B}
7
- `)},fatal:(B,H=I.FatalError)=>{X.stderr.write(`${B}
8
- `),X.exit(H)},flush:async()=>{if(j.size>0)await Promise.allSettled([...j]);if(!W&&!T)return;try{let B=await $(),H=B.flush;if(typeof H==="function")await H.call(B)}catch{}}};async function a(...B){for(let H of B)await G.debug(H)}async function s(...B){let H=Y(...B);console.log(H),X.exit(I.FatalError)}async function t(...B){await G.debug(...B)}function d(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 r(B,H={}){let J=H.status??d(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 N(B,H,J){let Q=_(),U={event:H,traceId:Q?.requestId,...J};if(B==="warn")O(G.warn(`[${H}]`,U));else O(G[B](U))}var c={request(B,H,J,Q,U={}){let Z=J>=500?"error":J>=400?"warn":"info";N(Z,"http.request",{method:B,path:H,status:J,durationMs:Q,...U})},query(B,H,J={}){N("debug","db.query",{sql:B,durationMs:H,...J})},slowQuery(B,H,J={}){N("warn","db.slow_query",{sql:B,durationMs:H,...J})},job(B,H,J={}){N(H==="failed"?"error":"info",`job.${H}`,{jobName:B,...J})},cache(B,H,J={}){N("debug",`cache.${B}`,{key:H,...J})}};G.struct=c;export{o as withLogContext,c as struct,L as resolveLogSettings,r as report,S as renderNormalizedError,z as parseLogLevel,h as parseLogFormat,q as normalizeError,D as normalizeContext,$ as logger,G as log,_ as getLogContext,t as echo,a as dump,s as dd};
7
+ `)},fatal:(B,H=D.FatalError)=>{X.stderr.write(`${B}
8
+ `),X.exit(H)},flush:async()=>{if(j.size>0)await Promise.allSettled([...j]);if(!W&&!R)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 a(...B){let H=Y(...B);console.log(H),X.exit(D.FatalError)}async function t(...B){await G.debug(...B)}function d(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 r(B,H={}){let J=H.status??d(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 T(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 c={request(B,H,J,Q,U={}){let Z=J>=500?"error":J>=400?"warn":"info";T(Z,"http.request",{method:B,path:H,status:J,durationMs:Q,...U})},query(B,H,J={}){T("debug","db.query",{sql:B,durationMs:H,...J})},slowQuery(B,H,J={}){T("warn","db.slow_query",{sql:B,durationMs:H,...J})},job(B,H,J={}){T(H==="failed"?"error":"info",`job.${H}`,{jobName:B,...J})},cache(B,H,J={}){T("debug",`cache.${B}`,{key:H,...J})}};G.struct=c;export{o as withLogContext,c as struct,L as resolveLogSettings,r as report,I as renderNormalizedError,b as parseLogLevel,h as parseLogFormat,q as normalizeError,_ as normalizeContext,$ as logger,G as log,y as getLogContext,t as echo,s as dump,a as dd};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/logging",
3
3
  "type": "module",
4
- "version": "0.70.72",
4
+ "version": "0.70.74",
5
5
  "description": "The Stacks logging system.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -53,12 +53,12 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@stacksjs/clarity": "^0.3.24",
56
- "@stacksjs/cli": "0.70.72",
57
- "@stacksjs/config": "0.70.72",
58
- "@stacksjs/error-handling": "0.70.72",
59
- "@stacksjs/path": "0.70.72",
60
- "@stacksjs/storage": "0.70.72",
61
- "@stacksjs/validation": "0.70.72",
62
- "typescript": "^6.0.2"
56
+ "@stacksjs/cli": "0.70.74",
57
+ "@stacksjs/config": "0.70.74",
58
+ "@stacksjs/error-handling": "0.70.74",
59
+ "@stacksjs/path": "0.70.74",
60
+ "@stacksjs/storage": "0.70.74",
61
+ "@stacksjs/validation": "0.70.74",
62
+ "typescript": "^7.0.2"
63
63
  }
64
64
  }