import type { LogContext, LogLevel, LogRecord, LogTransport } from '@stacksjs/types'; // Request context propagation for structured logging, and the transport // contract. Both are declared in `@stacksjs/types` so `config/logging.ts` can // reference them without importing this package, which would be a cycle. They // are re-exported here because this is where consumers already import them // from. export type { LogContext, LogLevel, LogRecord, LogTransport } from '@stacksjs/types'; /** * Attach a transport at runtime, and get back a function that detaches it. * * The alternative to declaring one in `config/logging.ts`, for a package that * has to attach without the application editing its config. Registering also * initializes the logger, so a transport attached at boot starts receiving * `debug` records immediately rather than waiting for the first console-visible * line to build the config. */ export declare function registerTransport(transport: LogTransport): () => void; /** The transports currently attached. A copy, so callers cannot mutate the list. */ export declare function transports(): readonly LogTransport[]; /** * Parse + validate `LOG_LEVEL` (stacksjs/stacks#1932). Previously the * env value was cast `as any` straight into the logger, so a typo * (`LOG_LEVEL=infoo`) silently produced undefined behavior. Now an * unknown value warns once and falls back. Accepts `warn` as an alias * for clarity's `warning`. */ export declare function parseLogLevel(raw: string | undefined, fallback?: LogLevel): LogLevel; /** Parse + validate `LOG_FORMAT`; defaults to json in prod, text in dev. */ export declare function parseLogFormat(raw: string | undefined): LogFormat; /** * Resolve the effective logger settings with precedence * **env var > config file > default** (stacksjs/stacks#1935). Pure + * exported so the precedence is unit-testable without booting the * singleton logger. */ export declare function resolveLogSettings(input: { envLevel?: string envFormat?: string cfgLevel?: string cfgFormat?: string cfgWriteToFile?: boolean isProduction?: boolean }): ResolvedLogSettings; export declare function normalizeError(err: unknown, depth?: number): NormalizedError; /** Render a normalized error (+ its cause chain) to a printable string. */ export declare function renderNormalizedError(n: NormalizedError): string; /** Apply {@link normalizeContextValue} to a structured log context. */ export declare function normalizeContext(ctx: LogContext): LogContext; /** * Run a function with an attached log context (e.g., request ID). * Use in HTTP middleware to propagate context through the request lifecycle. */ export declare function withLogContext(context: LogContext, fn: () => T): T; /** * Get the current log context (if any), with the active trace id folded in. * * The trace is read from the router's own AsyncLocalStorage through the * process-global symbol it publishes, rather than by importing * `@stacksjs/router` - which would be a cycle, since the router imports this. * That symbol is already a deliberate cross-copy contract (see * `request-context.ts`), so reading it here is using the seam rather than * reaching around one. * * Why it belongs here at all: a log line without a request id is a log line * nobody can join to anything. The id follows a request into its jobs now, and * the only place that becomes *useful* is the log. * * An explicit context wins, so a caller who sets `trace_id` deliberately - a * migration script correlating to a deploy, say - is not overwritten by an * ambient one. */ export declare function getLogContext(): LogContext | undefined; declare function getLogger(): Promise; // Helper function to format message for logging, including request context. // Exported for direct unit testing of arg handling (stacksjs/stacks#2047). export declare function formatMessage(...args: unknown[]): string; // Export convenience functions export declare function dump(...args: any[]): Promise; export declare function dd(...args: any[]): Promise; export declare function echo(...args: any[]): Promise; /** * Single error→log chokepoint (stacksjs/stacks#1933) — Laravel's * `report()`. Every automatic error-logging path (router action catch, * request catch, process-level handlers) funnels through here so the * policy lives in one place: * * - **4xx** (client errors — a thrown `HttpError(404)` / `422`) are * NOT reported at error level; they're expected control flow, not * server faults. Logged at debug so they stay traceable without * spamming the error stream. * - **5xx** and any non-HTTP throw are always reported at `error` * with the full normalized stack + cause chain + request context. * * Fire-and-forget by design (callers are on a response / exit path); * the write is queued through the shared logger so a flush-on-exit * (stacksjs/stacks#1934) drains it. */ export declare function report(error: unknown, options?: ReportOptions): void; declare function emit(level: 'debug' | 'info' | 'warn' | 'error', event: string, fields: StructuredFields): void; export declare const log: Log; /** * @defaultValue * ```ts * { * request: (method: string, path: string, status: number, durationMs: number, fields?: StructuredFields) => void, * query: (sql: string, durationMs: number, fields?: StructuredFields) => void, * slowQuery: (sql: string, durationMs: number, fields?: StructuredFields) => void, * job: (name: string, phase: 'started' | 'succeeded' | 'failed' | 'released', fields?: StructuredFields) => void, * cache: (op: 'hit' | 'miss' | 'set' | 'del', key: string, fields?: StructuredFields) => void * } * ``` */ export declare const struct: { /** * HTTP request completed. `status` is the response status code, * `durationMs` the wall time from request start to response sent. */ request: (method: string, path: string, status: number, durationMs: number, fields?: StructuredFields) => void; /** * Database query completed. `durationMs` is the wall time at the * driver boundary. */ query: (sql: string, durationMs: number, fields?: StructuredFields) => void; /** * A slow query (over the slow-threshold) — emits at warn so it * surfaces in the default log filter. */ slowQuery: (sql: string, durationMs: number, fields?: StructuredFields) => void; /** * Queue job lifecycle event. `phase` is `'started' | 'succeeded' | * 'failed' | 'released'`. */ job: (name: string, phase: 'started' | 'succeeded' | 'failed' | 'released', fields?: StructuredFields) => void; /** * Cache hit/miss event for warm-path debugging. */ cache: (op: 'hit' | 'miss' | 'set' | 'del', key: string, fields?: StructuredFields) => void }; export declare interface ResolvedLogSettings { level: LogLevel format: LogFormat writeToFile: boolean } /** * Normalize any thrown value into a stable, serializable shape * (stacksjs/stacks#1932). `JSON.stringify(new Error())` yields `{}`, * dropping the stack/message — so historically `log.error('x', err)` * lost the error entirely. This walks `.cause` (bounded) and always * captures name/message/stack. */ export declare interface NormalizedError { name: string message: string stack?: string cause?: NormalizedError } export declare interface Log { info: (...args: unknown[]) => Promise success: (msg: string) => Promise error: (message: string | Error | unknown, error?: unknown, context?: LogContext) => Promise warn: (arg: string, context?: unknown) => Promise warning: (arg: string) => Promise debug: (...args: unknown[]) => Promise dump: (...args: unknown[]) => Promise dd: (...args: unknown[]) => Promise echo: (...args: unknown[]) => Promise time: (label: string) => (metadata?: LogContext) => Promise syncWarn: (msg: string) => void syncError: (msg: string) => void fatal: (msg: string, exitCode?: number) => never flush: () => Promise exit: (msg?: string, exitCode?: number) => Promise } /** * Options for the fatal path: `log.error(message, { shouldExit: true })`. * * Kept as an explicit object shape rather than folded into a union with * `Error`. The old union included `| any`, which collapsed the whole thing and * let `log.error(msg, anything)` type check while silently dropping the error * (stacksjs/stacks#1932). For ordinary reporting use * `log.error(message, error?, context?)`. */ export declare interface LogErrorOptions { shouldExit: boolean silent?: boolean message?: ErrorMessage } export declare interface ReportOptions { status?: number context?: LogContext label?: string } /** * Structured logging shorthands for common framework events. * * The bare `log.info("…")` form is good for ad-hoc messages, but the * framework emits a predictable set of events (HTTP requests, DB * queries, queued jobs, cache operations) that benefit from a stable * shape so downstream log shippers can index on consistent field * names. * * Each helper: * 1. Attaches the current trace id (if any) automatically * 2. Picks the appropriate severity based on outcome * 3. Emits a consistent JSON shape in production (`event`, `level`, * `traceId`, …) while keeping the human-readable form in dev * * Helpers are batched onto `log.struct` so they don't pollute the * top-level `log` namespace, and so users can opt out by routing * `log.struct` to a custom transport in tests. */ declare interface StructuredFields { [key: string]: unknown } export type LogFormat = 'json' | 'text'; export type ErrorMessage = string; // Export logger getter for debugging export { getLogger as logger };