# @mifi/logger Intentional, namespaced logging for TypeScript browser and Node.js applications. ```ts import { createLogger } from "@mifi/logger"; const logger = createLogger({ environment: "production", namespace: "WIDGET" }); logger.child("Button").debug("Rendered", () => ({ expensive: "only evaluated when shown" })); ``` ## Install ```bash pnpm add @mifi/logger ``` Requires Node.js `>=24`. Published to the private `@mifi` registry. ## Quick start ```ts import { createLogger } from "@mifi/logger"; const logger = createLogger({ environment: "development", // "development" | "staging" | "production" namespace: "API", // optional; string or string[] joined with ":" }); logger.trace("very detailed"); logger.debug("diagnostic", () => ({ snapshot: heavyWork() })); logger.info("ready"); logger.warn("slow response", { ms: 1200 }); logger.error("request failed", error); logger.assert(userId, "missing user id"); const auth = logger.child("auth"); // namespace → "API:auth" auth.info("token refreshed"); ``` Console utility methods (`group`, `groupCollapsed`, `table`, `time`, `count`, `dir`, …) are available and gated by the same level policy as `info`. ## Runtime policy Default levels by environment: | Environment | Default level | Console shows | | ------------- | ------------- | --------------- | | `development` | `trace` | all levels | | `staging` | `warn` | `warn`, `error` | | `production` | `error` | `error` only | Levels in order (most → least verbose): `trace` → `debug` → `info` → `warn` → `error` → `silent`. ### Override precedence Highest wins: 1. Explicit `level` option on `createLogger` 2. Browser `sessionStorage.showLoggingFor` 3. Env vars `MIFI_LOG_LEVEL` / `MIFI_LOG_NAMESPACES` 4. Environment default (table above) ### Browser session override ```js sessionStorage.setItem("showLoggingFor", "debug"); // or restrict to namespaces (exact or descendants): sessionStorage.setItem("showLoggingFor", "debug:WIDGET,API"); ``` `WIDGET` matches `WIDGET` and `WIDGET:Button`. ### Container / Node env overrides See [Environment variables](#environment-variables) and [`env.example`](./env.example). ```bash MIFI_LOG_LEVEL=debug MIFI_LOG_NAMESPACES=WIDGET,API ``` ## Environment variables These are read from `process.env` (or from `options.env` when testing/injecting): | Variable | Description | | --------------------- | --------------------------------------------------------------------------- | | `MIFI_LOG_LEVEL` | Override level: `trace`, `debug`, `info`, `warn`, `error`, or `silent`. | | `MIFI_LOG_NAMESPACES` | Optional comma-separated namespace filters applied with the level override. | When only `MIFI_LOG_LEVEL` is set, all namespaces are enabled at that level. When `MIFI_LOG_NAMESPACES` is also set, only matching namespaces (and their children) emit at that level. Copy [`env.example`](./env.example) as a starting point for local or container config. ## Sentry The Sentry adapter has **no SDK dependency**. Provide the SDK your application already uses via `@mifi/logger/sentry`. **Development never sends to Sentry.** Staging and production attach the sink when `sentry` is configured. Destination policy when `sentry` is configured and `sinks` is not: | Runtime + environment | Console | Sentry | | ---------------------- | ---------------------- | -------------------------------- | | Production **browser** | Off | Issues for errors; optional Logs | | Production **Node** | On (stderr for errors) | Issues for errors; optional Logs | | Staging | On (default `warn`+) | Issues for errors; optional Logs | | Development | On | Nothing | ### Issues vs Logs - **Errors** → Sentry Issues (`captureException` / `captureMessage`). Not also written to Logs. - **Warnings and lower** → Sentry Logs (`sentry.logger.*`) only when `{ logs: true }` on the sink (or forced per call). Default Sentry Logs thresholds when `logs` is enabled: | Environment | Logs threshold | | ----------- | -------------- | | Staging | `info` | | Production | `warn` | Override with `logLevel` on `createSentrySink`. Console policy stays independent — production browsers can stay quiet while warnings still appear in Sentry Logs. ### Per-call options `trace` / `debug` / `log` / `info` / `warn` / `error` (and `assert`) accept a trailing options object. Console utilities (`group`, `time`, …) do not. ```ts import * as Sentry from "@sentry/nextjs"; import { createLogger } from "@mifi/logger"; import { createSentrySink } from "@mifi/logger/sentry"; const logger = createLogger({ environment: "production", namespace: "API", sentry: createSentrySink(Sentry, { logs: true }), }); logger.error("Request failed", error, { requestId: "req_1" }); logger.warn("slow", { ms: 1200 }); // → Sentry Logs in production logger.info("investigating", { orderId }, { sentry: true }); // force Logs logger.error("expected", err, { suppressSentry: true }); // console only (when enabled) ``` | Option | Effect | | ---------------------- | ------------------------------------------------------------------------ | | `sentry: true` | Force this event to Sentry Logs (ignores Logs threshold; not for errors) | | `suppressSentry: true` | Skip creating a Sentry Issue for an error | A last argument is treated as options only when every key is `sentry` or `suppressSentry`. Prefer the third-argument form when you also pass data. Logs payload: string message + flat primitive attributes (including `logger.namespace`). Nested objects are not unfurled. ## Custom sinks Replace the default destinations entirely with `sinks`: ```ts import { createLogger, createConsoleSink, type LogSink } from "@mifi/logger"; const analyticsSink: LogSink = { emit(event) { if (event.sendToConsole) analytics.track("log", event); }, }; const logger = createLogger({ environment: "production", sinks: [createConsoleSink(), analyticsSink], }); ``` When `sinks` is provided, the `sentry` option is not used for default sink selection. ## API overview | Export | From | Purpose | | ---------------------- | --------------------- | ------------------------------------------------ | | `createLogger` | `@mifi/logger` | Build a namespaced, Console-shaped logger | | `createConsoleSink` | `@mifi/logger` | Default styled console destination | | `parseLoggingOverride` | `@mifi/logger` | Parse `debug` / `debug:NS1,NS2` override strings | | `createSentrySink` | `@mifi/logger/sentry` | Adapter for any Sentry-like SDK | Types: `Logger`, `LoggerOptions`, `LogLevel`, `LoggerEnvironment`, `LogEvent`, `LogSink`, `LogData`, `LogCallOptions`, and (from `/sentry`) `SentryLike`, `SentryScopeLike`, `SentrySink`, `SentrySinkOptions`. Optional destinations live under `src/sinks/` (console, Sentry today; New Relic / Datadog-style adapters can land beside them later). Public import paths stay `@mifi/logger` and `@mifi/logger/sentry`. All public APIs include JSDoc with parameter and example documentation in the TypeScript sources. ## Lazy arguments Any function passed **after** the first argument is invoked only if the event is emitted: ```ts logger.debug("state", () => buildHugeSnapshot()); // skipped when debug is suppressed ``` ## Releases Woodpecker verifies pull requests and `main`, reports CI to Mattermost, then automatically releases from Conventional Commits merged to `main`. It updates `package.json` and `CHANGELOG.md`, creates a `vX.Y.Z` tag, and publishes to the private `@mifi` registry. Use `fix:` for a patch, `feat:` for a minor release, and `feat!` / `fix!` (or a `BREAKING CHANGE:` footer) for a major release. Commits such as `docs:`, `test:`, and `chore:` do not release a package. The release workflow uses the existing global `gitea_package_token`, `gitea_registry_username`, `gitea_release_token`, `mattermost_bot_access_token`, `mattermost_tests_channel_id`, `mattermost_pushes_channel_id`, and `mattermost_post_api_url` secrets.