206 lines
8.4 KiB
Markdown
206 lines
8.4 KiB
Markdown
# @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 `!` 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.
|