/** * Structured logger for opensip-tools. * * Outputs JSON log lines with: * - ts: ISO timestamp * - level: debug | info | warn | error * - evt: event name (e.g., 'cli.start', 'cli.check.complete') * - runId: correlation ID for the current CLI invocation * - msg: human-readable message * - ...data: additional structured fields * * Destinations: * - File: /opensip-tools/.runtime/logs/{YYYY-MM-DD}.jsonl * The CLI bootstrap supplies this path via configureLogger({ logDir }). * Without that, file output is disabled — user-global state * (`~/.opensip-tools/`) is reserved for config.yml only. * - stderr: when debug mode is enabled (Ink renders to stdout, logs to stderr) * * The `silent: true` option only suppresses stderr output, NOT file output. * * Two access patterns: * * 1. The exported `logger` singleton + `configureLogger(opts)`. Used * by the CLI bootstrap and any production caller that wants the * process-wide configuration. The four prior free mutators * (`setSilent`, `setDebugMode`, `setRunId`, `initLogFile`) were * collapsed into `configureLogger` in T1 deferred Item C. * * 2. The exported `LoggerImpl` class. Used by tests (or tools that * need an isolated logger) to construct a fresh instance whose * state is independent of the singleton. */ /** Structured logger surface; accepts a message string or a structured record. */ export interface Logger { debug(msgOrObj: string | Record, data?: Record): void; info(msgOrObj: string | Record, data?: Record): void; warn(msgOrObj: string | Record, data?: Record): void; error(msgOrObj: string | Record, data?: Record): void; } /** Log severity levels, ordered from most to least verbose. */ export type LogLevel = 'debug' | 'info' | 'warn' | 'error'; /** * Concrete logger implementation. Production code uses the exported * `logger` singleton (typed as the `Logger` interface so the * configuration surface is hidden from generic call sites); tests * (or tools that need an isolated logger) can construct a fresh * `LoggerImpl()` to exercise the logger without polluting (or being * polluted by) the singleton's state. * * @remarks Treat this class as advanced / discouraged for general * production use — the `Logger` interface is the seam. Importing * `LoggerImpl` is appropriate for tests and for tools that genuinely * need an isolated logger; everywhere else the typed `logger` * singleton is the right import. */ /** * Construction-time options for `LoggerImpl`. Also the shape accepted * by `configureLogger(opts)`, the single bootstrap-time configuration * seam that replaced the four free mutators (`setSilent`, * `setDebugMode`, `setRunId`, `initLogFile`) — T1 deferred Item C. */ export interface LoggerOptions { /** Initial log level. Defaults to `'warn'`. */ readonly level?: LogLevel; /** Suppress stderr output (file output still occurs). Defaults to `false`. */ readonly silent?: boolean; /** Enable debug-level output to stderr. Defaults to `false`. */ readonly debugMode?: boolean; /** Correlation id for the current CLI invocation. */ readonly runId?: string; /** * Directory the daily `.jsonl` log file is written to. When provided, * the logger initialises the file path and prunes logs older than * 7 days. Best-effort; failures are swallowed. */ readonly logDir?: string; } /** * Optional indirection for the runId on each log entry. The CLI binds * this to `() => currentScope()?.runId` at module init (in run-scope.ts, * which already depends on logger.ts — this preserves the dependency * direction and avoids a logger→run-scope import cycle that depcruise * would reject). Returns `undefined` when no scope is bound, in which * case `LoggerImpl.log` falls back to its instance-level `runId`. * * Tests that construct an isolated `new LoggerImpl()` skip this path * entirely — they call `setRunId(...)` on the instance. */ export type RunIdProvider = () => string | undefined; /** Concrete logger writing JSONL to stderr and an optional daily file. */ export declare class LoggerImpl implements Logger { private currentLevel; private silent; private debugMode; private runId; private logFilePath; private runIdProvider; constructor(opts?: LoggerOptions); /** * Apply a `LoggerOptions` bag to this instance. Used by the singleton * via `configureLogger(opts)` — the bootstrap-time configuration seam * that collapsed the four prior free mutators into one shot. Each * field is independent: an `applyOptions({ silent: true })` leaves * `debugMode` and `runId` alone. */ applyOptions(opts: LoggerOptions): void; debug(msgOrObj: string | Record, data?: Record): void; info(msgOrObj: string | Record, data?: Record): void; warn(msgOrObj: string | Record, data?: Record): void; error(msgOrObj: string | Record, data?: Record): void; /** * Suppress stderr output. File output still occurs. Used by the CLI * to silence the logger during Ink renders (Ink owns stdout; stderr * is reserved for `--debug` traces). Tests use this on fresh * `new LoggerImpl()` instances to verify the silent-mode contract. */ setSilent(value: boolean): void; /** * Enable debug-level output to stderr. Sets the current level to * `'debug'` when enabled. Disabling does NOT restore a prior level. */ setDebugMode(value: boolean): void; /** Set the correlation id stamped on each log entry. */ setRunId(id: string): void; getRunId(): string | undefined; /** * Inject a runId source consulted on every `log()`. Lets the kernel * route the singleton through the RunScope-bound runId without the * logger module having to import run-scope.ts (which would create a * cycle, since run-scope already imports the logger). */ setRunIdProvider(provider: RunIdProvider | undefined): void; /** * Initialize the log file for this instance. * * Writes to `/.jsonl`; the CLI bootstrap supplies * the path from `resolveProjectPaths(cwd).logsDir`. Without a call * to this function, file output is disabled (logs still hit stderr * in debug mode). * * Prunes log files older than 7 days inside the chosen directory. * * @internal — production callers route through `configureLogger`'s * `logDir` option. The method is `private` from a domain-design * standpoint but TypeScript can't mark it `private` because the * constructor calls it via `applyOptions`. */ initLogFile(dir: string): void; private shouldLog; private shouldWriteToFile; private log; } export declare const logger: Logger; /** * One-shot configuration for the process-wide `logger` singleton. * Replaces the four free mutators that previously each mutated one * field. The CLI's pre-action-hook calls this once with all relevant * options after flags are parsed and the project context is resolved. * * SaaS hosts that run multiple invocations concurrently should NOT use * this — they construct per-invocation `new LoggerImpl({...})` and * wire it into the `RunScope.logger` field so each run has its own * file path and runId. */ export declare function configureLogger(opts: LoggerOptions): void; //# sourceMappingURL=logger.d.ts.map