/** * @primitiv/logger * * Isomorphic structured logger for Primitiv. * Zero dependency, works in Node.js and browsers. * * Features: * - Hierarchical namespaces (`server:network:webrtc`) * - Log levels: trace, debug, info, warn, error * - Per-namespace level filtering with glob patterns * - Pluggable handlers (console, file, remote, custom) * - Client-scoped child loggers (`log.forClient(7, 'alice')`) * - Lazy evaluation for zero-cost filtered logs * - Ring buffer for post-mortem debugging * - Structured LogEntry objects */ /** Log severity levels (ordered from most to least verbose) */ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'silent'; /** A structured log entry produced by every log call */ interface LogEntry { /** Monotonic timestamp (ms) - `performance.now()` when available, else `Date.now()` */ timestamp: number; /** Current tick number (set automatically when a tick provider is registered) */ tick?: number; /** Severity level */ level: LogLevel; /** Hierarchical namespace (e.g. `'server:network:webrtc'`) */ namespace: string; /** Human-readable message */ message: string; /** Optional structured data attached to the log */ data?: unknown; /** Client ID (set automatically by `forClient()` child loggers) */ clientId?: number; /** Username (set automatically by `forClient()` child loggers) */ username?: string; } /** A handler that receives structured log entries */ type LogHandler = (entry: LogEntry) => void; /** * A namespaced, leveled logger. * * Created via `Logger.create('namespace')` or `parentLogger.child('suffix')`. * Never instantiate directly. */ declare class Logger { /** The hierarchical namespace (e.g. `'server:network'`) */ readonly namespace: string; /** Cached numeric level for fast comparison */ private _levelValue; /** Per-client metadata (set by `forClient()`) */ private _clientId?; private _username?; private constructor(); /** * Create or retrieve a logger for the given namespace. * * ```ts * const log = Logger.create('server:network'); * log.info('Listening', { port: 3000 }); * ``` */ static create(namespace: string): Logger; /** * Create a child logger with an appended namespace segment. * * ```ts * const net = Logger.create('server:network'); * const ws = net.child('websocket'); * // ws.namespace === 'server:network:websocket' * ``` */ child(suffix: string): Logger; /** * Create a child logger scoped to a specific client session. * The returned logger automatically attaches `clientId` and `username` * to every log entry. * * ```ts * const clientLog = serverLog.forClient(7, 'alice'); * clientLog.info('Connected'); * // -> { namespace: 'server:client:7', clientId: 7, username: 'alice', ... } * ``` */ forClient(clientId: number, username?: string): Logger; /** * Dispose a client logger when the client disconnects. * Removes it from the global registry so it can be garbage collected. */ static dispose(namespace: string): void; trace(message: string | (() => string), data?: unknown): void; debug(message: string | (() => string), data?: unknown): void; info(message: string | (() => string), data?: unknown): void; warn(message: string | (() => string), data?: unknown): void; error(message: string | (() => string), data?: unknown): void; /** Check if a given level would pass the current filter */ isEnabled(level: LogLevel): boolean; /** @internal - called by the global recalc */ _setResolvedLevel(level: LogLevel): void; private _emit; /** * Set the global minimum log level. * All namespaces without a specific override will use this level. * * ```ts * Logger.setLevel('warn'); // only warn + error globally * ``` */ static setLevel(level: LogLevel): void; /** * Set the minimum log level for a specific namespace pattern. * * ```ts * Logger.setLevel('server:client:*', 'debug'); * Logger.setLevel('render:gl', 'trace'); * ``` */ static setLevel(pattern: string, level: LogLevel): void; /** Remove all namespace-specific level overrides */ static clearLevelOverrides(): void; /** * Enable a namespace pattern (sets it to `'trace'` - everything passes). * Shorthand for `Logger.setLevel(pattern, 'trace')`. */ static enable(pattern: string): void; /** * Disable a namespace pattern (sets it to `'silent'` - nothing passes). */ static disable(pattern: string): void; /** Add a log handler. Returns a dispose function to remove it. */ static addHandler(handler: LogHandler): () => void; /** Remove all handlers */ static clearHandlers(): void; /** Get the current number of handlers */ static get handlerCount(): number; /** * Register a function that returns the current tick number. * The returned value is automatically attached to every `LogEntry`. * * Call with `null` to clear the provider. * * ```ts * Logger.setTickProvider(() => server.tickCount); * ``` */ static setTickProvider(provider: (() => number) | null): void; /** Reset all state: handlers, levels, loggers. Mainly for tests. */ static reset(): void; } /** * Console handler for @primitiv/logger. * * Formats log entries with colors and writes them to the appropriate * console method. Detects Node.js vs browser automatically. */ /** * Console handler - auto-selects Node.js (ANSI) or browser (CSS %c) formatting. * * Usage: * ```ts * import { Logger, consoleHandler } from '@primitiv/logger'; * Logger.addHandler(consoleHandler); * ``` */ declare const consoleHandler: LogHandler; /** * Null handler - silently discards all entries. Useful for tests. */ declare const nullHandler: LogHandler; /** * Ring buffer for post-mortem log capture. * * Keeps the last N log entries in a circular buffer. When the buffer is full, * the oldest entry is silently overwritten. This enables "dump last N logs" * after an error without paying the cost of keeping all logs in memory. * * Usage: * ```ts * import { Logger } from '@primitiv/logger'; * import { RingBuffer } from '@primitiv/logger'; * * const ring = new RingBuffer(500); * Logger.addHandler(ring.handler); * * // Later, after an error: * const recentLogs = ring.drain(); * ``` */ declare class RingBuffer { private _buffer; private _head; private _count; readonly capacity: number; constructor(capacity?: number); /** A LogHandler that pushes entries into this ring buffer */ readonly handler: LogHandler; /** Number of entries currently stored */ get size(): number; /** * Drain all entries from the buffer in chronological order. * Clears the buffer afterward. */ drain(): LogEntry[]; /** * Peek at all entries without clearing. * Returns entries in chronological order. */ peek(): LogEntry[]; /** Clear all entries */ clear(): void; } export { Logger, RingBuffer, consoleHandler, nullHandler }; export type { LogEntry, LogHandler, LogLevel };