diff --git a/.changeset/capture-output-own-events.md b/.changeset/capture-output-own-events.md deleted file mode 100644 index 8f5c32f9a..000000000 --- a/.changeset/capture-output-own-events.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Fix `captureOutput` in `evlog/next/instrumentation` sending evlog's own events to drains a second time: in JSON mode, each wide event printed to stdout came back as a `log.info` event, and error events as `log.error` through stderr. evlog now marks its own writes so the capture passes them through. diff --git a/.changeset/cli-config-module-options.md b/.changeset/cli-config-module-options.md deleted file mode 100644 index c8170e5e8..000000000 --- a/.changeset/cli-config-module-options.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@evlog/cli": minor ---- - -`evlog config` reads the options passed to the evlog module in `nuxt.config.ts` or `nitro.config.ts`, which override `evlog.config.ts` at runtime. A note under the file name says so, and each setting they replace shows the value that applies with its line, and the value of the file under it. `--json` adds `moduleOptions` and an `overrides: { value, source }` field on those settings. diff --git a/.changeset/cli-config.md b/.changeset/cli-config.md deleted file mode 100644 index 00be21705..000000000 --- a/.changeset/cli-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@evlog/cli": minor ---- - -`evlog map` and `evlog logs` read `evlog.config.ts` (or `.mts`, `.js`, `.mjs`), the nearest one from the package up to the workspace root, without running it. `map.rules` turns checks off or back on for every entry point, `map.ignore` leaves entry points out by file glob and `--json` reports how many as `ignored`, `map.minScore` and `map.baseline` gate like `--min-score` and `--baseline`, and `logs.dir` and `logs.limit` set the defaults of `--dir` and `--limit`. A flag wins over the config. The config can extend a local file or a published preset, one level deep. `evlog config` prints the settings that apply, grouped into service, sampling, redaction, pipeline and CLI, with the file and line each one comes from and evlog's defaults where the file says nothing, `--json` for the same as JSON, and `evlog doctor` reports a config it cannot read. A misspelt setting, an `extends` written as a path instead of an imported config, a value under `map` or `logs` computed at runtime, or turning off `wide-event` or `context` stops the command with a `cli.CONFIG_*` error. The CLI now needs `evlog` 2.31.0 or later. diff --git a/.changeset/cli-init-config.md b/.changeset/cli-init-config.md deleted file mode 100644 index c3245796d..000000000 --- a/.changeset/cli-init-config.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@evlog/cli": minor ---- - -`evlog init` writes `evlog.config.ts` with the service name, drains, enrichers and sampling you pick, dev and production branched in one place. Nuxt and Nitro load it through the module, and on Next.js, Hono, Express and Fastify the generated `evlog.ts` imports it. In a workspace with a root `evlog.config.ts`, the app config extends it and leaves out what the root already sets. A config you already have is read and never rewritten: a setting it lacks becomes a manual step, and the service it names becomes the default answer. diff --git a/.changeset/cli-local-first-handoff.md b/.changeset/cli-local-first-handoff.md deleted file mode 100644 index dcc413250..000000000 --- a/.changeset/cli-local-first-handoff.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@evlog/cli": minor ---- - -A global `evlog` now hands off to the `@evlog/cli` installed in the project it runs in, so a project always runs the CLI version it pins. In a directory with no install, the global copy runs as before. diff --git a/.changeset/client-min-level-empty.md b/.changeset/client-min-level-empty.md deleted file mode 100644 index 7aa8fb7b3..000000000 --- a/.changeset/client-min-level-empty.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Fix every client log being silently dropped in Nuxt when `minLevel` is not set: the public runtime config turns the unset value into `''`, which no level passed. The client logger now falls back to `'debug'` for any value that is not a valid level. diff --git a/.changeset/config-file-runtime.md b/.changeset/config-file-runtime.md deleted file mode 100644 index 45ae7c1b2..000000000 --- a/.changeset/config-file-runtime.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"evlog": minor ---- - -The Nuxt and Nitro modules (v2 and v3) load `evlog.config.ts` (or `.mts`, `.js`, `.mjs`), the nearest one from the app up to the workspace root, and bundle it into the server. Options passed to the module, or set under the `evlog` key in `nuxt.config.ts`, override the file with the same merge as `extends`. The `drain`, `enrich` and `keep` of the file run next to the `evlog:drain`, `evlog:enrich` and `evlog:emit:keep` hooks, and a drain built with `createDrainPipeline()` is flushed when the server closes. On Nuxt the browser logger takes `enabled`, `pretty` and `minLevel` from the file too, unless the `evlog` key in `nuxt.config.ts` or a `NUXT_PUBLIC_EVLOG_*` variable sets them. `defineEvlogHook()` from `evlog/eve` takes the settings of `evlog.config.ts`, so an Eve agent spreads its config into the hook: `defineEvlogHook({ ...config, message: 'preview' })`. Its logger settings start the logger on the first turn, and `init` still replaces them when given. diff --git a/.changeset/define-evlog-extends.md b/.changeset/define-evlog-extends.md deleted file mode 100644 index a07da693e..000000000 --- a/.changeset/define-evlog-extends.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"evlog": minor ---- - -`defineEvlog()` accepts `extends`, another config to build on, such as a preset published to npm. Objects merge key by key with the config winning on each key, lists are replaced except `redact.paths`, `redact.patterns` and `sampling.keep`, which add up, `plugins` merge by `name`, and a config extends one level only. `mergeEvlogConfig()` applies the same merge outside `defineEvlog()`. `EvlogConfig` also takes `map` and `logs`, the settings `evlog map` and `evlog logs` read from `evlog.config.ts`, and `toLoggerConfig()` and `toMiddlewareOptions()` leave them out. diff --git a/.changeset/drop-h3-options-cause.md b/.changeset/drop-h3-options-cause.md deleted file mode 100644 index 265d345e4..000000000 --- a/.changeset/drop-h3-options-cause.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop wide events from repeating an error made with h3's `createError({ statusCode, message })` under `error.cause`. h3 keeps that options object as `cause`, so the event carried the message and status code twice. evlog now drops the `cause` when it only restates fields the error already has, and keeps it when it carries anything else. diff --git a/.changeset/evlog-agents-skill-targets.md b/.changeset/evlog-agents-skill-targets.md deleted file mode 100644 index 412554f80..000000000 --- a/.changeset/evlog-agents-skill-targets.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@evlog/cli': minor ---- - -`evlog agents` and `evlog init` now install the agent skills globally by default, for `universal` (the shared `.agents/skills` directory that Codex and the other agents read) and `claude-code` (a symlink into it). Pass `--local` to install into the project instead, with `.agents/skills` and `.claude/skills` at the project root. - -Breaking: `--global` is removed, since global is now the default. `evlog agents` no longer writes `CLAUDE.md`. Most agents read `AGENTS.md` directly, and Claude Code does too unless a `CLAUDE.md` sits above it. Add a one-line `@AGENTS.md` yourself if you want one; an existing `CLAUDE.md` is left alone. diff --git a/.changeset/fork-await-drain.md b/.changeset/fork-await-drain.md deleted file mode 100644 index e1c25e87d..000000000 --- a/.changeset/fork-await-drain.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"evlog": minor ---- - -`log.fork()` returns a promise that resolves once the child event has drained, and never rejects. Await it when a write in the child, such as an audit routed to `auditOnly(sink, { await: true })`, must land before the response. With `waitUntil` set, `log.fork()` registers the child with it when called, so an unawaited fork drains before the runtime stops and `ForkLifecycle.onChildExit` fires after the child's drain. A failure after the child event is emitted, such as a throwing console write, is now logged with `console.error` instead of being dropped. Forks you leave unawaited need `void log.fork(...)` under a `no-floating-promises` lint rule. diff --git a/.changeset/init-skills-already-once.md b/.changeset/init-skills-already-once.md deleted file mode 100644 index 7424a525a..000000000 --- a/.changeset/init-skills-already-once.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@evlog/cli': patch ---- - -Print the "evlog skills already installed" line once in `evlog init` and `evlog agents`. The plan already lists it, so the status line below now shows only the `npx skills update` refresh command. diff --git a/.changeset/map-logger-set-and-empty-score.md b/.changeset/map-logger-set-and-empty-score.md deleted file mode 100644 index 184304889..000000000 --- a/.changeset/map-logger-set-and-empty-score.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@evlog/cli': patch ---- - -Fix two `evlog map` results. `useLogger(event).set({ … })` written inline, without binding the logger to a variable, is now credited as request context, so the `context` rule no longer reports the handler as missing `log.set()`. A project with no entry points to score is now graded `unscored` ("nothing to scan") instead of a free "excellent" 100, and `--min-score` exits 1 in that case because there is nothing to meet the threshold against. diff --git a/.changeset/map-unscored-banner.md b/.changeset/map-unscored-banner.md deleted file mode 100644 index c9dc45d61..000000000 --- a/.changeset/map-unscored-banner.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@evlog/cli': patch ---- - -`evlog map` no longer draws a score for an unscored scan. A project with no entry points to score showed a full banner reading `100` and a full bar, next to "nothing to scan". The banner now draws `--` with an empty bar, and `--all` shows `--` in place of `100/100`. The JSON output and the `--min-score` gate were already correct and are unchanged. diff --git a/.changeset/next-instrumentation-bundling.md b/.changeset/next-instrumentation-bundling.md deleted file mode 100644 index 3279a9a82..000000000 --- a/.changeset/next-instrumentation-bundling.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Fix every route returning 500 with `Cannot find package 'evlog'` in a Next.js app built with Turbopack and deployed without `node_modules` (Vercel, `output: 'standalone'`) when root `instrumentation.ts` uses `defineNodeInstrumentation({ ... })`. The gate loaded `evlog/next/instrumentation/create` through a computed `import()` that the build left out of the output file trace. It now imports the module behind `process.env.NEXT_RUNTIME === 'nodejs'`, so Next.js bundles it into the Node.js build and drops it from the Edge build. With webpack, the module used to load unbundled from `node_modules`, where `NEXT_RUNTIME` is unset, so `captureOutput` never applied. It applies now. diff --git a/.changeset/next-stream-after-scope.md b/.changeset/next-stream-after-scope.md deleted file mode 100644 index 327829c35..000000000 --- a/.changeset/next-stream-after-scope.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop `evlog/next` from logging "`after` was called outside a request scope" when a streamed response (SSE, NDJSON, AI SDK streams) completes or the client disconnects. A streamed response now registers its `waitUntil` while the handler is still running, and the drain started when the body finishes joins that registration. The same applies to any integration given a `waitUntil` option, such as Hono and Cloudflare Workers. diff --git a/.changeset/nitro-no-duplicate-error-log.md b/.changeset/nitro-no-duplicate-error-log.md deleted file mode 100644 index ed2800292..000000000 --- a/.changeset/nitro-no-duplicate-error-log.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop Nitro and Nuxt from printing an error a second time when evlog already records it. In production, an unhandled error or a `fatal` one produced both the wide event and Nitro's `[request error] [unhandled]` block with its stack. evlog's error handler now skips Nitro's default logging whenever the request has a wide event. Nitro still logs the error when evlog is disabled, the route is excluded, or the plugin is not registered. diff --git a/.changeset/nitro-skip-default-log-expected-errors.md b/.changeset/nitro-skip-default-log-expected-errors.md deleted file mode 100644 index 08530d8f9..000000000 --- a/.changeset/nitro-skip-default-log-expected-errors.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop Nitro and Nuxt from printing expected `createError` 4xx responses as `[request error] [unhandled]` in production. The wide event already records these errors, so evlog's Nitro error handler now skips Nitro's default logging for `EvlogError` with a status below 500. Server errors and errors that are not from evlog are still logged by Nitro. diff --git a/.changeset/print-client-ingest-events.md b/.changeset/print-client-ingest-events.md deleted file mode 100644 index 218e7e61a..000000000 --- a/.changeset/print-client-ingest-events.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Client events received by the Nuxt and Nitro ingest endpoint are now printed to stdout, the same way server events are. Before, they were only sent to drains, so an app without a drain accepted browser logs with a 204 and they never showed up anywhere. Events still reach your drains, and `silent: true` still suppresses the console output. diff --git a/.changeset/redact-client-ingest.md b/.changeset/redact-client-ingest.md deleted file mode 100644 index 9f6fe87d9..000000000 --- a/.changeset/redact-client-ingest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Redact client events received by the Nuxt and Nitro ingest endpoint. The route built its own wide event and skipped the global `redact` config, so in production a browser event carrying an email or a card number reached stdout and every drain unmasked, while server events were redacted. Client events now go through the same redaction before enrichers, stdout and drains. diff --git a/.changeset/redact-email-pnpm-paths.md b/.changeset/redact-email-pnpm-paths.md deleted file mode 100644 index e02e3b81b..000000000 --- a/.changeset/redact-email-pnpm-paths.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Fix the built-in `email` redaction masking pnpm store paths in stack traces. Segments such as `node_modules/.pnpm/h3@1.15.11/...` were rewritten to `h***@***.11`, so drained `error.stack` values lost the package name and version. The email pattern now requires a letters-only top-level domain, so real addresses are still masked as `a***@***.com`. diff --git a/.changeset/redact-secret-keys.md b/.changeset/redact-secret-keys.md deleted file mode 100644 index 224de6a7a..000000000 --- a/.changeset/redact-secret-keys.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'evlog': minor -'@evlog/cli': patch ---- - -Redact secrets by key name. A new `secrets` built-in, on by default with the other built-ins, replaces the value of any key ending in `password`, `passwd`, `secret` or `token` (case-insensitive, at any depth) with `replacement`. So `password`, `newPassword`, `client_secret` and `accessToken` no longer reach production logs, while counters such as `totalTokens` and `tokensPerSecond` are kept. Values are replaced in full, booleans and `null` are left alone, and listing `builtins` without `'secrets'` turns it off. `evlog config` lists `secrets` among the built-ins. diff --git a/.changeset/regexp-redact-patterns-bridge.md b/.changeset/regexp-redact-patterns-bridge.md deleted file mode 100644 index 0c3ea1feb..000000000 --- a/.changeset/regexp-redact-patterns-bridge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Fix custom RegExp `redact.patterns` declared in `nuxt.config.ts` (or Nitro module options) being silently dropped: patterns are now serialized as `{ source, flags }` across the config bridges and rebuilt into RegExp on the server. A pattern object without a `source` field is reported instead of skipped, and a function-valued `redact.replacement` / `redact.transform` in the Nuxt module now prints the same warning the standalone Nitro modules already print. diff --git a/.changeset/request-logger-debug.md b/.changeset/request-logger-debug.md deleted file mode 100644 index 9fe5e6f1e..000000000 --- a/.changeset/request-logger-debug.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Add `debug()` to the request logger. `useLogger(event).debug('...')` threw `log.debug is not a function`, though the generated `AGENTS.md` tells agents to use `log.debug()` for step detail. It now records a `debug` entry in `requestLogs` and merges its context, without raising the event level, and the `evlog/vite` plugin strips it from production builds like the global `log.debug()`. diff --git a/.changeset/sample-client-ingest.md b/.changeset/sample-client-ingest.md deleted file mode 100644 index 7b98f2337..000000000 --- a/.changeset/sample-client-ingest.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': minor ---- - -Apply `sampling.rates` to browser logs received on the client ingest endpoint. Client events previously skipped head sampling entirely, so `info: 0` still sent every client info log to your drains. A sampled-out client event is now dropped before enrichment and draining, with the same defaults as server events: errors and fatal are kept, trace is dropped unless `sampling.rates.trace` is set. If you tuned rates for server traffic only, check that they still fit your client volume. diff --git a/.changeset/silent-drain-warning-nitro.md b/.changeset/silent-drain-warning-nitro.md deleted file mode 100644 index 453fbf956..000000000 --- a/.changeset/silent-drain-warning-nitro.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"evlog": patch ---- - -Nuxt and Nitro apps that set `silent: true` in the module options no longer print `silent mode is enabled but no drain is configured` at startup. The logger starts from those options before the server plugins attach the drain, so the warning fired even with an `evlog:drain` hook. Standalone `initLogger({ silent: true })` without a drain still warns. diff --git a/.changeset/skip-transport-success-request-log.md b/.changeset/skip-transport-success-request-log.md deleted file mode 100644 index 6c4e44cfb..000000000 --- a/.changeset/skip-transport-success-request-log.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop the Nuxt client transport endpoint from emitting a request wide event for every batch of client logs. A successful `POST /api/_evlog/ingest` (204) no longer adds a server event next to each `source: "client"` event, in drains or on the console. Failed transport requests (4xx and 5xx) still emit, so a broken transport stays visible. A custom `transport.endpoint` is honoured, and an explicit `exclude` pattern still excludes the route. diff --git a/.changeset/unwrap-h3-error-cause.md b/.changeset/unwrap-h3-error-cause.md deleted file mode 100644 index a649b9533..000000000 --- a/.changeset/unwrap-h3-error-cause.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'evlog': patch ---- - -Stop wide events from repeating the whole error under `error.cause` when h3 wraps a thrown error. h3 keeps the original error as `cause` and copies its message and stack, so every error event carried the message, stack and payload twice. evlog now serializes the original error once and keeps the h3 status fields (`statusCode`, `statusMessage`, `data`). A `cause` that is a different error is still kept. diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 129583d88..0c3424caf 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,31 @@ # @evlog/cli +## 0.10.0 + +### Minor Changes + +- [#807](https://github.com/evloghq/evlog/pull/807) [`7e6282a`](https://github.com/evloghq/evlog/commit/7e6282ad8ad43d440285930b63c6c2ff635282ef) Thanks [@HugoRCD](https://github.com/HugoRCD)! - `evlog config` reads the options passed to the evlog module in `nuxt.config.ts` or `nitro.config.ts`, which override `evlog.config.ts` at runtime. A note under the file name says so, and each setting they replace shows the value that applies with its line, and the value of the file under it. `--json` adds `moduleOptions` and an `overrides: { value, source }` field on those settings. + +- [#806](https://github.com/evloghq/evlog/pull/806) [`704c311`](https://github.com/evloghq/evlog/commit/704c311569adcbf84ef7b49529d2d4727023724e) Thanks [@HugoRCD](https://github.com/HugoRCD)! - `evlog map` and `evlog logs` read `evlog.config.ts` (or `.mts`, `.js`, `.mjs`), the nearest one from the package up to the workspace root, without running it. `map.rules` turns checks off or back on for every entry point, `map.ignore` leaves entry points out by file glob and `--json` reports how many as `ignored`, `map.minScore` and `map.baseline` gate like `--min-score` and `--baseline`, and `logs.dir` and `logs.limit` set the defaults of `--dir` and `--limit`. A flag wins over the config. The config can extend a local file or a published preset, one level deep. `evlog config` prints the settings that apply, grouped into service, sampling, redaction, pipeline and CLI, with the file and line each one comes from and evlog's defaults where the file says nothing, `--json` for the same as JSON, and `evlog doctor` reports a config it cannot read. A misspelt setting, an `extends` written as a path instead of an imported config, a value under `map` or `logs` computed at runtime, or turning off `wide-event` or `context` stops the command with a `cli.CONFIG_*` error. The CLI now needs `evlog` 2.31.0 or later. + +- [#808](https://github.com/evloghq/evlog/pull/808) [`09b8d50`](https://github.com/evloghq/evlog/commit/09b8d50c69c0ca5073eaff433d476c5354a0420d) Thanks [@HugoRCD](https://github.com/HugoRCD)! - `evlog init` writes `evlog.config.ts` with the service name, drains, enrichers and sampling you pick, dev and production branched in one place. Nuxt and Nitro load it through the module, and on Next.js, Hono, Express and Fastify the generated `evlog.ts` imports it. In a workspace with a root `evlog.config.ts`, the app config extends it and leaves out what the root already sets. A config you already have is read and never rewritten: a setting it lacks becomes a manual step, and the service it names becomes the default answer. + +- [#833](https://github.com/evloghq/evlog/pull/833) [`62637e9`](https://github.com/evloghq/evlog/commit/62637e97bab1d6be5fc99219cc9ff5f14de4d0df) Thanks [@evlogai](https://github.com/apps/evlogai)! - A global `evlog` now hands off to the `@evlog/cli` installed in the project it runs in, so a project always runs the CLI version it pins. In a directory with no install, the global copy runs as before. + +- [#831](https://github.com/evloghq/evlog/pull/831) [`b7b8c8b`](https://github.com/evloghq/evlog/commit/b7b8c8b4d200b33f5c118a0d2450af4d51d0469e) Thanks [@evlogai](https://github.com/apps/evlogai)! - `evlog agents` and `evlog init` now install the agent skills globally by default, for `universal` (the shared `.agents/skills` directory that Codex and the other agents read) and `claude-code` (a symlink into it). Pass `--local` to install into the project instead, with `.agents/skills` and `.claude/skills` at the project root. + + Breaking: `--global` is removed, since global is now the default. `evlog agents` no longer writes `CLAUDE.md`. Most agents read `AGENTS.md` directly, and Claude Code does too unless a `CLAUDE.md` sits above it. Add a one-line `@AGENTS.md` yourself if you want one; an existing `CLAUDE.md` is left alone. + +### Patch Changes + +- [#824](https://github.com/evloghq/evlog/pull/824) [`75a32d8`](https://github.com/evloghq/evlog/commit/75a32d8b98dd321d0579e67f168ca3600b3d81b7) Thanks [@evlogai](https://github.com/apps/evlogai)! - Print the "evlog skills already installed" line once in `evlog init` and `evlog agents`. The plan already lists it, so the status line below now shows only the `npx skills update` refresh command. + +- [#822](https://github.com/evloghq/evlog/pull/822) [`2d7503a`](https://github.com/evloghq/evlog/commit/2d7503a1dac7adf2a735e80bc3a676fe4ea6a36e) Thanks [@evlogai](https://github.com/apps/evlogai)! - Fix two `evlog map` results. `useLogger(event).set({ … })` written inline, without binding the logger to a variable, is now credited as request context, so the `context` rule no longer reports the handler as missing `log.set()`. A project with no entry points to score is now graded `unscored` ("nothing to scan") instead of a free "excellent" 100, and `--min-score` exits 1 in that case because there is nothing to meet the threshold against. + +- [#823](https://github.com/evloghq/evlog/pull/823) [`b603f28`](https://github.com/evloghq/evlog/commit/b603f2828bcb52001d79f398c4df0970383fe786) Thanks [@evlogai](https://github.com/apps/evlogai)! - `evlog map` no longer draws a score for an unscored scan. A project with no entry points to score showed a full banner reading `100` and a full bar, next to "nothing to scan". The banner now draws `--` with an empty bar, and `--all` shows `--` in place of `100/100`. The JSON output and the `--min-score` gate were already correct and are unchanged. + +- [#836](https://github.com/evloghq/evlog/pull/836) [`f3613ee`](https://github.com/evloghq/evlog/commit/f3613ee77be5f7eceec3d818af2a4307254d01bf) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Redact secrets by key name. A new `secrets` built-in, on by default with the other built-ins, replaces the value of any key ending in `password`, `passwd`, `secret` or `token` (case-insensitive, at any depth) with `replacement`. So `password`, `newPassword`, `client_secret` and `accessToken` no longer reach production logs, while counters such as `totalTokens` and `tokensPerSecond` are kept. Values are replaced in full, booleans and `null` are left alone, and listing `builtins` without `'secrets'` turns it off. `evlog config` lists `secrets` among the built-ins. + ## 0.9.0 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index 5ea19be49..ad22702fd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@evlog/cli", - "version": "0.9.0", + "version": "0.10.0", "description": "The official command line for evlog", "author": "HugoRCD ", "homepage": "https://evlog.dev", diff --git a/packages/evlog/CHANGELOG.md b/packages/evlog/CHANGELOG.md index be83b2bed..d36480f1e 100644 --- a/packages/evlog/CHANGELOG.md +++ b/packages/evlog/CHANGELOG.md @@ -1,5 +1,51 @@ # evlog +## 2.31.0 + +### Minor Changes + +- [#807](https://github.com/evloghq/evlog/pull/807) [`7e6282a`](https://github.com/evloghq/evlog/commit/7e6282ad8ad43d440285930b63c6c2ff635282ef) Thanks [@HugoRCD](https://github.com/HugoRCD)! - The Nuxt and Nitro modules (v2 and v3) load `evlog.config.ts` (or `.mts`, `.js`, `.mjs`), the nearest one from the app up to the workspace root, and bundle it into the server. Options passed to the module, or set under the `evlog` key in `nuxt.config.ts`, override the file with the same merge as `extends`. The `drain`, `enrich` and `keep` of the file run next to the `evlog:drain`, `evlog:enrich` and `evlog:emit:keep` hooks, and a drain built with `createDrainPipeline()` is flushed when the server closes. On Nuxt the browser logger takes `enabled`, `pretty` and `minLevel` from the file too, unless the `evlog` key in `nuxt.config.ts` or a `NUXT_PUBLIC_EVLOG_*` variable sets them. `defineEvlogHook()` from `evlog/eve` takes the settings of `evlog.config.ts`, so an Eve agent spreads its config into the hook: `defineEvlogHook({ ...config, message: 'preview' })`. Its logger settings start the logger on the first turn, and `init` still replaces them when given. + +- [#805](https://github.com/evloghq/evlog/pull/805) [`7951563`](https://github.com/evloghq/evlog/commit/7951563d3a86335249d08b4c4e55ccd8e92d5ff0) Thanks [@HugoRCD](https://github.com/HugoRCD)! - `defineEvlog()` accepts `extends`, another config to build on, such as a preset published to npm. Objects merge key by key with the config winning on each key, lists are replaced except `redact.paths`, `redact.patterns` and `sampling.keep`, which add up, `plugins` merge by `name`, and a config extends one level only. `mergeEvlogConfig()` applies the same merge outside `defineEvlog()`. `EvlogConfig` also takes `map` and `logs`, the settings `evlog map` and `evlog logs` read from `evlog.config.ts`, and `toLoggerConfig()` and `toMiddlewareOptions()` leave them out. + +- [#834](https://github.com/evloghq/evlog/pull/834) [`b1880ab`](https://github.com/evloghq/evlog/commit/b1880ab766d1da012795bd11727b7ee6c06ec72c) Thanks [@HugoRCD](https://github.com/HugoRCD)! - `log.fork()` returns a promise that resolves once the child event has drained, and never rejects. Await it when a write in the child, such as an audit routed to `auditOnly(sink, { await: true })`, must land before the response. With `waitUntil` set, `log.fork()` registers the child with it when called, so an unawaited fork drains before the runtime stops and `ForkLifecycle.onChildExit` fires after the child's drain. A failure after the child event is emitted, such as a throwing console write, is now logged with `console.error` instead of being dropped. Forks you leave unawaited need `void log.fork(...)` under a `no-floating-promises` lint rule. + +- [#836](https://github.com/evloghq/evlog/pull/836) [`f3613ee`](https://github.com/evloghq/evlog/commit/f3613ee77be5f7eceec3d818af2a4307254d01bf) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Redact secrets by key name. A new `secrets` built-in, on by default with the other built-ins, replaces the value of any key ending in `password`, `passwd`, `secret` or `token` (case-insensitive, at any depth) with `replacement`. So `password`, `newPassword`, `client_secret` and `accessToken` no longer reach production logs, while counters such as `totalTokens` and `tokensPerSecond` are kept. Values are replaced in full, booleans and `null` are left alone, and listing `builtins` without `'secrets'` turns it off. `evlog config` lists `secrets` among the built-ins. + +- [#811](https://github.com/evloghq/evlog/pull/811) [`567bc62`](https://github.com/evloghq/evlog/commit/567bc62ddd87342d20fc7a42745521beb561879d) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Apply `sampling.rates` to browser logs received on the client ingest endpoint. Client events previously skipped head sampling entirely, so `info: 0` still sent every client info log to your drains. A sampled-out client event is now dropped before enrichment and draining, with the same defaults as server events: errors and fatal are kept, trace is dropped unless `sampling.rates.trace` is set. If you tuned rates for server traffic only, check that they still fit your client volume. + +### Patch Changes + +- [#816](https://github.com/evloghq/evlog/pull/816) [`db19fc2`](https://github.com/evloghq/evlog/commit/db19fc241b6521f561e351263e8c7fb780f0dadf) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Fix `captureOutput` in `evlog/next/instrumentation` sending evlog's own events to drains a second time: in JSON mode, each wide event printed to stdout came back as a `log.info` event, and error events as `log.error` through stderr. evlog now marks its own writes so the capture passes them through. + +- [#810](https://github.com/evloghq/evlog/pull/810) [`d476140`](https://github.com/evloghq/evlog/commit/d476140b5ec8c39380d0582db3ecbb21cc626d0e) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Fix every client log being silently dropped in Nuxt when `minLevel` is not set: the public runtime config turns the unset value into `''`, which no level passed. The client logger now falls back to `'debug'` for any value that is not a valid level. + +- [#829](https://github.com/evloghq/evlog/pull/829) [`b49915d`](https://github.com/evloghq/evlog/commit/b49915dd8ce7518ea313ea589f1c92f1ffd10ddc) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Stop wide events from repeating an error made with h3's `createError({ statusCode, message })` under `error.cause`. h3 keeps that options object as `cause`, so the event carried the message and status code twice. evlog now drops the `cause` when it only restates fields the error already has, and keeps it when it carries anything else. + +- [#816](https://github.com/evloghq/evlog/pull/816) [`db19fc2`](https://github.com/evloghq/evlog/commit/db19fc241b6521f561e351263e8c7fb780f0dadf) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Fix every route returning 500 with `Cannot find package 'evlog'` in a Next.js app built with Turbopack and deployed without `node_modules` (Vercel, `output: 'standalone'`) when root `instrumentation.ts` uses `defineNodeInstrumentation({ ... })`. The gate loaded `evlog/next/instrumentation/create` through a computed `import()` that the build left out of the output file trace. It now imports the module behind `process.env.NEXT_RUNTIME === 'nodejs'`, so Next.js bundles it into the Node.js build and drops it from the Edge build. With webpack, the module used to load unbundled from `node_modules`, where `NEXT_RUNTIME` is unset, so `captureOutput` never applied. It applies now. + +- [#830](https://github.com/evloghq/evlog/pull/830) [`d419b9c`](https://github.com/evloghq/evlog/commit/d419b9cd7070bf992adcc7ab977b51cf4a6acdf4) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Stop `evlog/next` from logging "`after` was called outside a request scope" when a streamed response (SSE, NDJSON, AI SDK streams) completes or the client disconnects. A streamed response now registers its `waitUntil` while the handler is still running, and the drain started when the body finishes joins that registration. The same applies to any integration given a `waitUntil` option, such as Hono and Cloudflare Workers. + +- [#837](https://github.com/evloghq/evlog/pull/837) [`b3b3b35`](https://github.com/evloghq/evlog/commit/b3b3b35a7cba1bdba7c84e824483048b03bcf92f) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Stop Nitro and Nuxt from printing an error a second time when evlog already records it. In production, an unhandled error or a `fatal` one produced both the wide event and Nitro's `[request error] [unhandled]` block with its stack. evlog's error handler now skips Nitro's default logging whenever the request has a wide event. Nitro still logs the error when evlog is disabled, the route is excluded, or the plugin is not registered. + +- [#819](https://github.com/evloghq/evlog/pull/819) [`e7c954b`](https://github.com/evloghq/evlog/commit/e7c954bf5adfb3befa4e676342db89eab0ddc85d) Thanks [@evlogai](https://github.com/apps/evlogai)! - Stop Nitro and Nuxt from printing expected `createError` 4xx responses as `[request error] [unhandled]` in production. The wide event already records these errors, so evlog's Nitro error handler now skips Nitro's default logging for `EvlogError` with a status below 500. Server errors and errors that are not from evlog are still logged by Nitro. + +- [#821](https://github.com/evloghq/evlog/pull/821) [`6887f13`](https://github.com/evloghq/evlog/commit/6887f13327b617e256115fab2c4872c7ef7e8993) Thanks [@evlogai](https://github.com/apps/evlogai)! - Client events received by the Nuxt and Nitro ingest endpoint are now printed to stdout, the same way server events are. Before, they were only sent to drains, so an app without a drain accepted browser logs with a 204 and they never showed up anywhere. Events still reach your drains, and `silent: true` still suppresses the console output. + +- [#838](https://github.com/evloghq/evlog/pull/838) [`94043b1`](https://github.com/evloghq/evlog/commit/94043b18926d10e6ec3823e361c478cc2c1e846a) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Redact client events received by the Nuxt and Nitro ingest endpoint. The route built its own wide event and skipped the global `redact` config, so in production a browser event carrying an email or a card number reached stdout and every drain unmasked, while server events were redacted. Client events now go through the same redaction before enrichers, stdout and drains. + +- [#817](https://github.com/evloghq/evlog/pull/817) [`038de6a`](https://github.com/evloghq/evlog/commit/038de6a0c041a16bef228dcb243aac968053228a) Thanks [@evlogai](https://github.com/apps/evlogai)! - Fix the built-in `email` redaction masking pnpm store paths in stack traces. Segments such as `node_modules/.pnpm/h3@1.15.11/...` were rewritten to `h***@***.11`, so drained `error.stack` values lost the package name and version. The email pattern now requires a letters-only top-level domain, so real addresses are still masked as `a***@***.com`. + +- [#794](https://github.com/evloghq/evlog/pull/794) [`bd3558d`](https://github.com/evloghq/evlog/commit/bd3558dbcc56965d8ff2d57ff4d2965db7b4280b) Thanks [@evlogai](https://github.com/apps/evlogai)! - Fix custom RegExp `redact.patterns` declared in `nuxt.config.ts` (or Nitro module options) being silently dropped: patterns are now serialized as `{ source, flags }` across the config bridges and rebuilt into RegExp on the server. A pattern object without a `source` field is reported instead of skipped, and a function-valued `redact.replacement` / `redact.transform` in the Nuxt module now prints the same warning the standalone Nitro modules already print. + +- [#835](https://github.com/evloghq/evlog/pull/835) [`e02edd1`](https://github.com/evloghq/evlog/commit/e02edd171d9adaac9f04e9e75e021df85ce5c82c) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Add `debug()` to the request logger. `useLogger(event).debug('...')` threw `log.debug is not a function`, though the generated `AGENTS.md` tells agents to use `log.debug()` for step detail. It now records a `debug` entry in `requestLogs` and merges its context, without raising the event level, and the `evlog/vite` plugin strips it from production builds like the global `log.debug()`. + +- [#804](https://github.com/evloghq/evlog/pull/804) [`49af75e`](https://github.com/evloghq/evlog/commit/49af75eb53e3bb9bb0642c607328b04d9f0472a0) Thanks [@HugoRCD](https://github.com/HugoRCD)! - Nuxt and Nitro apps that set `silent: true` in the module options no longer print `silent mode is enabled but no drain is configured` at startup. The logger starts from those options before the server plugins attach the drain, so the warning fired even with an `evlog:drain` hook. Standalone `initLogger({ silent: true })` without a drain still warns. + +- [#826](https://github.com/evloghq/evlog/pull/826) [`670db27`](https://github.com/evloghq/evlog/commit/670db2784f026145a0f44b6130bd641959f0d53c) Thanks [@evlogai](https://github.com/apps/evlogai)! - Stop the Nuxt client transport endpoint from emitting a request wide event for every batch of client logs. A successful `POST /api/_evlog/ingest` (204) no longer adds a server event next to each `source: "client"` event, in drains or on the console. Failed transport requests (4xx and 5xx) still emit, so a broken transport stays visible. A custom `transport.endpoint` is honoured, and an explicit `exclude` pattern still excludes the route. + +- [#825](https://github.com/evloghq/evlog/pull/825) [`2bbd392`](https://github.com/evloghq/evlog/commit/2bbd39292bef4e53f3d6bf341687b86145881761) Thanks [@evlogai](https://github.com/apps/evlogai)! - Stop wide events from repeating the whole error under `error.cause` when h3 wraps a thrown error. h3 keeps the original error as `cause` and copies its message and stack, so every error event carried the message, stack and payload twice. evlog now serializes the original error once and keeps the h3 status fields (`statusCode`, `statusMessage`, `data`). A `cause` that is a different error is still kept. + ## 2.30.1 ### Patch Changes diff --git a/packages/evlog/package.json b/packages/evlog/package.json index e3d222ae7..2f4a4e7bc 100644 --- a/packages/evlog/package.json +++ b/packages/evlog/package.json @@ -1,6 +1,6 @@ { "name": "evlog", - "version": "2.30.1", + "version": "2.31.0", "description": "Modern TypeScript logger — simple logs, wide events, structured errors. Built for scripts, libraries, jobs, edge, and HTTP. One drain pipeline everywhere.", "author": "HugoRCD ", "homepage": "https://evlog.dev",