# Logging standard Lumi uses the structured logger in `src/services/logger.js` for operational events. It stores searchable entries for the admin UI, attaches request context, redacts common credential fields, and publishes live admin updates. ## Operational logging Core services create a named logger: ```js const { createLogger } = require("./logger"); const logger = createLogger("core:updater", { category: "updates" }); logger.info("Update check completed", { version }, { event: "update_check_completed" }); ``` Plugins receive a plugin-scoped `logger` in their `init()` dependencies. Use that injected logger when practical. A plugin module that must log outside `init()` may create a logger named `plugin:`. Use these source prefixes consistently: - `core:` for Lumi core and WebUI services. - `platform:` for Discord, Twitch, YouTube, and similar integrations. - `plugin:` for bundled and locally installed plugins. - `companion:` for the desktop Companion host. Give each logger a stable default category such as `lifecycle`, `http`, `integration`, `command`, `automation`, `security`, `updates`, or `plugin`. Every operational call must provide a stable, lowercase `snake_case` event ID. Messages are for people and may improve over time; event IDs are for filtering and automation and should remain stable. Use: - `debug` for detailed, low-value troubleshooting information. - `info` for meaningful lifecycle and administrative actions. - `warn` for degraded behavior that Lumi can continue through. - `error` for a failed operation that may require attention. Pass an `Error` as the details argument when one is available. The logger preserves its stack trace while applying credential redaction: ```js logger.error("Plugin refresh failed", error, { event: "plugin_refresh_failed" }); ``` The metadata argument only accepts `source`, `category`, `event`, and `requestId`. Operational fields such as `plugin_id`, `user_id`, `status`, or `duration_ms` belong in the details argument: ```js logger.warn("Plugin health check degraded", { plugin_id: plugin.id, status: health.status }, { event: "plugin_health_degraded" }); ``` Do not include passwords, tokens, cookies, pairing secrets, authorization headers, signature fragments, full request bodies, or full third-party payloads in messages or details. Record a bounded summary with IDs, status, counts, and safe error text instead. Redaction is a safety net, not a reason to collect secrets. ## Metrics and high-frequency events Do not write high-frequency counters or per-frame events to the operational log. There is no global metrics API. A feature may retain its own bounded metrics only when it also owns the storage, retention, and inspection UI. Lumi AI is one example: `plugins/lumi_ai/backend/metrics.js` aggregates AI timings and retains bounded work history. Operational failures in such a feature still belong in the core logger. Feature-owned diagnostic logs follow the same credential and payload rules. They must have explicit size/age retention, sanitize recursively before writing, and remain admin-only. Transcription worker diagnostics are kept separately because they are high-volume troubleshooting data; they are not a substitute for operational warnings and errors in the core logger. The desktop Companion cannot write directly to the server database. Its local JSON Lines logs therefore carry the same `level`, `source`, `category`, `event`, and human-readable `message` shape, use the shared `CompanionLogSanitizer`, and enforce local retention. The native OBS Bridge must use OBS's `blog()` facility so its messages remain in the operator's OBS log. Prefix each message with `[Lumi Companion]` and a stable `event=` field, and never include IPC payloads or credentials. ## Console output Direct `console.*` calls are captured after `hookConsole()` starts, but they lose the useful source and event metadata of a named logger. Prefer a named logger in runtime code, including startup and shutdown paths. Console output remains reasonable in standalone verification and build scripts where the terminal is the intended consumer. Browser-side console diagnostics and isolated worker sandboxes are outside the server operational-log boundary. ## Retention and context Operational logs default to 30 days and at most 100,000 entries. Request correlation is inherited through `withLogContext()` and logger `run()` scopes. The logger redacts common sensitive keys and credential-looking text before an entry is stored or published.