# evlog > A modern TypeScript logger for everything you ship. Simple structured logs, wide events, and structured errors in one API - for scripts, libraries, jobs, edge, and requests. ## Resources for Agents - [API catalog: every service document this site publishes](https://www.evlog.dev/.well-known/api-catalog) - [Sitemap (XML)](https://www.evlog.dev/sitemap.xml) - [Sitemap (Markdown): every page on the site](https://www.evlog.dev/sitemap.md) - [MCP server card: MCP endpoint at https://www.evlog.dev/mcp](https://www.evlog.dev/.well-known/mcp/server-card.json) - [MCP endpoint (streamable HTTP)](https://www.evlog.dev/mcp) - [llms.txt: index of the documentation for LLMs](https://www.evlog.dev/llms.txt) - [llms-full.txt: the full documentation as a single file](https://www.evlog.dev/llms-full.txt) - [Agent skills index: every skill published by this site](https://www.evlog.dev/.well-known/skills/index.json) - [Agent skill: analyze-logs](https://www.evlog.dev/.well-known/skills/analyze-logs/SKILL.md) - [Agent skill: build-audit-logs](https://www.evlog.dev/.well-known/skills/build-audit-logs/SKILL.md) - [Agent skill: review-logging-patterns](https://www.evlog.dev/.well-known/skills/review-logging-patterns/SKILL.md) - [OpenAPI document: every route this site serves to agents](https://www.evlog.dev/openapi.json) Every page on this site is available as raw markdown: append `.md` to its URL or send `Accept: text/markdown`. # Docs MCP > Connect Cursor, Claude Code, or VS Code to https://www.evlog.dev/mcp and your agent reads the current evlog docs, and prices your log bill, from the editor. An agent answers evlog questions from its training data, which can be a release behind the package you installed. The docs MCP server gives it the current pages instead. Add `https://www.evlog.dev/mcp` to your editor: no account, no key, read-only, over [streamable HTTP](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http). ## Add it to your editor ### Cursor [Add to Cursor](https://www.evlog.dev/mcp/deeplink) opens Cursor with the server filled in. Every docs page carries the same link as **Add MCP Server** in the copy menu at the top. To add it by hand, create `.cursor/mcp.json` in your project, or `~/.cursor/mcp.json` for every project ([Cursor MCP docs](https://cursor.com/docs/mcp)): ```json [.cursor/mcp.json] { "mcpServers": { "evlog": { "url": "https://www.evlog.dev/mcp" } } } ``` ### Claude Code ```bash [Terminal] claude mcp add --transport http evlog https://www.evlog.dev/mcp ``` Add `--scope user` to make it available in every project ([Claude Code MCP docs](https://code.claude.com/docs/en/mcp)). ### VS Code [Install in VS Code](https://www.evlog.dev/mcp/deeplink?ide=vscode), run **MCP: Add Server** from the Command Palette and pick HTTP, or commit it for the team in `.vscode/mcp.json` ([VS Code MCP docs](https://code.visualstudio.com/docs/agent-customization/mcp-servers)): ```json [.vscode/mcp.json] { "servers": { "evlog": { "type": "http", "url": "https://www.evlog.dev/mcp" } } } ``` ### Any other client Point any MCP client that speaks streamable HTTP at `https://www.evlog.dev/mcp`. Opening that URL in a browser brings you back to this page. ## Tools All three are read-only. None of them reads your project: they take a page path or the numbers you pass.
| Tool | Returns | Input |
|---|---|---|
list-pages
|
Every docs page: title, path, description, URL | None |
get-page
|
The full markdown of one page, the same bytes as
/raw/
|
path
, e.g.
/start/installation
|
estimate-log-cost
|
A month of logs before and after evlog: events, GB, cost, and the basis used |
requestsPerMonth
, plus optional
linesPerRequest
(default 4),
provider
,
perGb
,
perMillionIndexed
,
keepPercent
(default 100)
|
| You want the agent to | Use |
|---|---|
| Look up a page in the middle of a task | This MCP server |
| Follow the evlog conventions and review logging code |
Agent skills
, installed with
npx skills add https://www.evlog.dev
|
| Read the docs in a tool that has no MCP support |
llms.txt
, or any page as markdown at
/raw/
|
Use
log
|
Use
createLogger()
/
createRequestLogger()
|
Use
useLogger(event)
|
|---|---|---|
| Quick one-off events | Scripts, jobs, workers, queues, HTTP without a framework | API routes with a framework integration |
| No context accumulation needed | Accumulate context over an operation | Retrieve the request-scoped logger |
| Client-side logging | Wide events (one log per operation) | Access the auto-managed wide event |
| Field | Required | Description |
|---|---|---|
message
|
Yes | What happened (user-facing) |
status
|
No | HTTP status code (default: 500) |
why
|
No | Technical reason (for debugging) |
fix
|
No | Actionable solution |
link
|
No | Documentation URL for more info |
cause
|
No | Original error (if wrapping) |
internal
|
No |
Backend-only fields for logs and wide events — never included in HTTP JSON or
parseError()
|
| Decision day | Cost to adopt evlog |
|---|---|
| Day 0 (greenfield) | Add the framework module. Done. |
| Day 30 (small app) | Switch the logger surface — about a day of work. |
| Day 365 (production app) | Walk the codebase to swap loggers, settle field-name conventions, fold in audit and redaction. The same cost as migrating between any two structured loggers. |
| Convention | Example | |
|---|---|---|
| Catalog key |
UPPER_SNAKE_CASE
(enum-style, scales to hundreds of entries)
|
PAYMENT_DECLINED
,
INVOICE_REFUND
|
| Prefix |
lower.dot.case
, can be hierarchical
|
'billing'
,
'billing.payment'
,
'auth.session'
|
| Wire format |
${prefix}.${KEY}
(preserved casing)
|
billing.PAYMENT_DECLINED
,
auth.INVALID_TOKEN
|
| One catalog = | One bounded context, one prefix, one file |
errors/billing.ts
,
audit/billing.ts
|
| Repo shape | Recommended location |
|---|---|
Single file (
src/errors.ts
)
|
At the bottom of the same file |
Folder (
src/errors/*.ts
)
|
In
src/errors/index.ts
(centralised) or each catalog file (decentralised)
|
| npm package |
At the bottom of the package's main
src/index.ts
so it ships in the published
.d.ts
|
| Monorepo | One augmentation per package, no central registry needed |
| Symbol | Kind | Purpose |
|---|---|---|
defineError(code, options)
|
factory | Standalone single-error factory. No prefix derivation. |
defineErrorCatalog(prefix, map)
|
factory | Bundle of typed errors sharing a prefix. |
defineAuditAction(action, opts?)
|
factory | Standalone single-action audit factory. |
defineAuditCatalog(prefix, map)
|
factory |
Bundle of typed audit actions sharing a prefix. Each entry accepts
target
,
description
,
severity
,
requiresChanges
,
requiresReason
,
redactPaths
.
|
AuditCatalogEntry
|
type |
Metadata shape for a single catalog entry (alias of
AuditActionDefinition
).
|
AuditSeverity
|
type |
'low' | 'medium' | 'high' | 'critical'
.
|
RegisteredErrorCatalogs
|
interface | Augmentable registry of error catalogs. |
RegisteredAuditCatalogs
|
interface | Augmentable registry of audit catalogs. |
ErrorCode
|
type | Union of all registered error codes. |
AuditAction
|
type | Union of all registered audit actions. |
| Stage |
log
(simple)
|
createLogger
/
createRequestLogger
|
Framework middleware |
|---|---|---|---|
| Create | Implicit per call |
createLogger({...})
or
createRequestLogger({...})
|
Auto on request start |
| Accumulate | N/A (single call) |
log.set()
multiple times
|
log.set()
via
useLogger(event)
|
| Emit | Immediate |
Manual
log.emit()
|
Auto on response end |
| Sample | Head sampling only | Head + tail sampling | Head + tail sampling |
| Enrich | Via global drain | Via global drain | Via hooks or callbacks |
| Drain | Via global drain | Via global drain | Via hooks or callbacks |
| Field | Source |
|---|---|
method
|
HTTP method (
GET
,
POST
, ...)
|
path
|
Request path |
requestId
|
Auto-generated UUID (or
cf-ray
on Cloudflare)
|
startTime
|
Date.now()
for duration calculation
|
| Enricher | Adds | Source |
|---|---|---|
| User Agent |
userAgent
(browser, OS, device)
|
User-Agent
header
|
| Geo |
geo
(country, region, city)
|
Platform headers (Vercel, Cloudflare) |
| Request Size |
requestSize
(request/response bytes)
|
Content-Length
headers
|
| Trace Context |
traceContext
(traceId, parentSpanId)
|
traceparent
header
|
| Order | Hook | When | Purpose |
|---|---|---|---|
| 1 |
evlog:emit:keep
|
After request ends, before sampling | Force-keep events based on outcome |
| 2 |
evlog:enrich
|
After emit, before console output and drain | Add derived context to the event |
| 3 |
evlog:drain
|
After enrichment | Send event to external services |
| Success | Error | |
|---|---|---|
| Trigger |
afterResponse
/
response
hook
|
error
hook
|
| Level |
info
(or
warn
if status >= 400)
|
error
|
| Status | From response |
From error's
status
field (default 500)
|
| Error context | None |
error
field with message, stack,
why
,
fix
|
| Double-emit guard |
Checks
_evlogEmitted
flag
|
Sets
_evlogEmitted = true
|
log
|
createLogger
/
createRequestLogger
|
Framework middleware | |
|---|---|---|---|
| Use case | Quick one-off events | Scripts, jobs, workers, queues, HTTP without a framework | API routes with a framework integration |
| Context | Single call |
Accumulate with
set()
|
Accumulate with
set()
|
| Emit | Immediate |
Manual
emit()
|
Automatic on response end |
| Lifecycle | None | You manage it | Framework manages it |
| Output | Console + drain | Console + drain | Console + drain + enrich |
| Context | Best fit | Why |
|---|---|---|
| HTTP route in Nuxt / Next / Hono / Express / … |
useLogger(event)
via
framework integration
|
One wide event per request, auto-emitted on response end |
| HTTP handler without a framework |
createRequestLogger({ method, path })
|
Same shape as framework middleware, manual emit |
| CLI tool / one-shot script |
log.*
for steps +
createLogger
for the run summary — see
Standalone
|
Pretty in dev, structured in CI, one summary event for the whole run |
| Published library |
createLogger
only — never
initLogger
— see
Standalone
|
Don't pollute the host app's global config or force a drain on consumers |
| Background job / queue worker / cron |
createLogger({ jobId, queue })
per invocation — see
Standalone
|
One wide event per job run, perfect for retry analysis |
| Cloudflare Worker / edge function |
createWorkersLogger(req)
or
createRequestLogger
— see
Cloudflare Workers
|
Per-request event, no
process
globals required
|
| AWS Lambda |
initLogger
once +
createLogger
per invocation — see
AWS Lambda
|
Cold-start init, per-event scope, drain flush in the handler |
| Batch / pipeline step |
createLogger({ step })
per stage
|
One event per stage with inputs and outputs side by side |
| AI agent / LLM call |
createLogger
+
createAILogger
|
Token usage, tool calls, streaming metrics on the same wide event |
| Library function called inside a request |
useLogger(event)
from caller, or accept a logger as argument
|
Inherit the parent's request context, contribute to the same wide event |
| Shared workspace package | Treat it like a library — see Standalone |
Host app owns
initLogger
/ drain; packages use
createLogger
or accept a logger
|
| Pattern | Example Input | Masked Output |
|---|---|---|
creditCard
|
4111111111111111
|
****1111
|
email
|
alice@example.com
|
a***@***.com
|
ipv4
|
192.168.1.100
|
***.***.***.100
|
phone
|
+33 6 12 34 56 78
|
+33 ****5678
|
jwt
|
eyJhbGciOiJIUzI1NiIs...
|
eyJ***.***
|
bearer
|
Bearer sk_live_abc123...
|
Bearer ***
|
iban
|
FR76 3000 6000 0112 ...189
|
FR76****189
|
| Key | Redacted |
|---|---|
password
,
newPassword
,
user_password
|
Yes |
secret
,
clientSecret
,
client_secret
|
Yes |
token
,
accessToken
,
refresh_token
,
API_TOKEN
|
Yes |
totalTokens
,
tokensPerSecond
,
tokenType
|
No |
| Pattern | Matches |
|---|---|
user.email
|
Exact path only |
password
or
**.password
|
password
key at any depth
|
*_token
|
Key names like
access_token
,
refresh_token
|
user.*
|
user.email
,
user.password
, etc.
|
audit.changes.*.password
|
Mixed exact + wildcard segments |
| Field | Type | Description |
|---|---|---|
path
|
string
|
Dot-notation path from the event root (
user.email
,
items.0.token
)
|
key
|
string
|
Leaf key of the field (
email
)
|
groups
|
string[]
|
Capture groups of the matching
patterns
entry. Only set for
patterns
|
| Option | Type | Default | Description |
|---|---|---|---|
redact
|
boolean | RedactConfig
|
true
in production
|
Enabled by default in production.
false
to disable. Object for fine-grained control
|
paths
|
string[]
|
undefined
|
Dot-notation paths with globs (
password
,
**.password
,
*_token
,
user.*
)
|
patterns
|
RegExp[]
|
undefined
|
Custom regex on string values. Uses flat
replacement
string
|
builtins
|
false | string[]
|
All enabled |
false
disables built-ins. Array selects specific ones
|
replacement
|
string | (matched, ctx) => string
|
'[REDACTED]'
|
Replacement for paths and custom patterns. Built-ins use smart masking instead. A function computes it from the matched value |
transform
|
(event) => void
|
undefined
|
Escape hatch for policies that are conditional, tenant-scoped, or allowlist-shaped. Runs before the declarative stages |
| Condition | Type | Description |
|---|---|---|
status
|
number
|
Keep if HTTP status >= value (e.g.,
400
catches all 4xx and 5xx)
|
duration
|
number
|
Keep if request duration >= value in milliseconds |
path
|
string
|
Keep if request path matches glob pattern (e.g.,
'/api/critical/**'
)
|
| Field | Type | Description |
|---|---|---|
status
|
number | undefined
|
HTTP response status |
duration
|
number | undefined
|
Request duration in ms |
path
|
string | undefined
|
Request path |
method
|
string | undefined
|
HTTP method |
context
|
Record
|
All fields set via
log.set()
|
shouldKeep
|
boolean
|
Set to
true
to force-keep
|
| Specifier | Formats the argument as |
|---|---|
%s
|
string, objects and arrays as JSON |
%d
|
number, coerced with
Number()
, bigints as
10n
|
%j
|
JSON, with a string fallback for values JSON cannot serialize |
| Level | Method | When to use |
|---|---|---|
fatal
|
log.fatal()
|
Unrecoverable failures: the request or process cannot continue. Always kept, even when sampling would drop it |
info
|
log.info()
|
Normal operations: startup, shutdown, successful actions |
warn
|
log.warn()
|
Unexpected but recoverable situations: cache miss, retry, deprecation |
error
|
log.error()
|
Failures that need attention: API errors, timeouts, invalid state |
debug
|
log.debug()
|
Development-only details: SQL queries, intermediate state, routing |
trace
|
log.trace()
|
Very fine-grained details below
debug
. Dropped by default; opt in with
sampling.rates.trace
|
| Field | Required | Description |
|---|---|---|
message
|
Yes | What happened (shown to users) |
code
|
No |
Stable machine-readable identifier for client branching (e.g.
'PAYMENT_DECLINED'
)
|
status
|
No | HTTP status code (default: 500) |
why
|
No | Technical reason (for debugging) |
fix
|
No | Actionable solution |
link
|
No | Documentation URL |
cause
|
No | Original error (for error chaining) |
data
|
No | Extra payload returned to the client (see below) |
internal
|
No | Backend-only context (see below) |
| Goal | Config |
|---|---|
One clean signal, wide event only, no Nitro
[request error]
overlay
|
dev: 'evlog'
(default in pretty dev)
|
| Wide event context + Nitro's native Youch stack (evlog prints Why/Fix only) |
dev: 'nitro'
|
| Full evlog block and Nitro overlay (debug) |
dev: 'both'
|
| No pretty tree (JSON logs) but still suppress Nitro overlay |
pretty: false
,
dev: { frameworkOverlay: false }
|
{{ parsed.why }}
{{ parsed.fix }}
| File | What happens |
|---|---|
AGENTS.md
|
Created if missing. Otherwise the evlog block is replaced in place, between
and
— everything outside those markers is left exactly as it was.
|
| Flag | What it does |
|---|---|
--skills
|
Comma-separated skill names, passed to
npx skills add --skill
(default: all of them)
|
--no-skills
|
Still write
AGENTS.md
; skip only the skill installation, so nothing is spawned
|
--local
|
Install the skills into this project instead of for every project (the default is global) |
--source
|
Where the skills are published — a plain http(s) origin (default:
https://www.evlog.dev
)
|
--dry-run
|
Show the plan without writing or running anything |
--yes
,
-y
|
Apply without confirming |
| Change | Verdict |
|---|---|
A requirement went from
pass
to
fail
|
Fails the gate |
| A passing requirement was disabled with a comment | Fails the gate |
| The global score dropped | Fails the gate |
| A new entry point was added with no instrumentation |
Listed under
NEW AND DARK
, does not fail
|
| An entry point was deleted | Counted as removed |
A requirement went from
fail
to
pass
|
Counted as fixed |
| Code | Meaning |
|---|---|
0
|
Score met the threshold, no regression against the baseline, or neither was requested |
1
|
Score below
--min-score
, a regression against
--baseline
, or the scan could not run
|
2
|
Usage error — unknown flag, invalid
--framework
, or a baseline whose rule set does not match the running CLI
|
| Setting | Accepts | Flag | What it does |
|---|---|---|---|
logs.dir
|
a path |
--dir
|
Reads this directory instead of
.evlog/logs
|
logs.limit
|
whole number of 1 or more |
--limit
|
Shows at most this many events |
| In the config | Result |
|---|---|
A scalar or a function:
service
,
drain
,
enrich
,
keep
|
The config's value replaces the preset's |
An object:
sampling.rates
,
routes
,
env
,
map.rules
|
Merged key by key, the config winning on each key |
redact.paths
,
redact.patterns
,
sampling.keep
|
The preset's entries, then the config's |
Any other list:
map.ignore
,
include
,
exclude
|
The config's list replaces the preset's |
plugins
|
Merged by
name
, a config plugin replacing the preset plugin of the same name
|
redact: false
|
Redaction off |
redact: true
|
The preset's redact settings, unchanged |
| Framework | How the config applies |
|---|---|
| Nuxt, Nitro, TanStack Start | The evlog module finds and loads it |
| Eve agents |
defineEvlogHook(config)
in
agent/hooks/evlog.ts
|
| Next.js |
createEvlog(config)
and
createInstrumentation(toLoggerConfig(config))
|
| Any other framework |
toLoggerConfig(config)
where you call
initLogger
,
toMiddlewareOptions(config)
where you register the middleware
|
| Code | Raised when |
|---|---|
cli.CONFIG_PARSE_FAILED
|
The file has a syntax error |
cli.CONFIG_NO_EXPORT
|
There is no default export of an object or
defineEvlog({ ... })
|
cli.CONFIG_NOT_STATIC
|
The default export,
extends
, or a
map
or
logs
value is computed at runtime
|
cli.CONFIG_INVALID
|
A setting is misspelt, has the wrong type, or turns off
wide-event
or
context
|
cli.CONFIG_EXTENDS_NOT_FOUND
|
The
extends
import does not lead to a file
|
cli.CONFIG_EXTENDS_DEPTH
|
The extended config extends another one |
cli.CONFIG_EXTENDS_STRING
|
extends
is a path string instead of an imported config
|
| Check | Passes when | Warns or fails when |
|---|---|---|
node
|
Node 22 or later | Older than 22 — the CLI will not run reliably (fail) |
project
|
A
package.json
was resolved
|
None found above the working directory |
stack
|
A framework was detected and evlog is installed | A framework was detected but evlog is not wired to it |
evlog
|
evlog
resolves from
node_modules
|
Declared in
package.json
but not installed, or missing entirely
|
config
|
evlog.config.ts
was read, and the config it extends was found
|
The file cannot be read: a syntax error, a misspelt setting, or a CLI setting computed at runtime (fail). The check is omitted when there is no config |
logs
|
A local drain exists, or the fs drain is wired (the directory appears on first write) | Never — the check is omitted when no local drain is configured |
| Flag | What it does |
|---|---|
--cwd
|
Diagnose another directory |
--json
|
Machine-readable output on stdout |
--debug
|
Print the steps the command went through |
--noHeader
|
Skip the branded header |
| Offer | Appears when |
|---|---|
| Error catalog |
The scan found the same
createError
in more than one file
|
| Audit actions |
map
flagged sensitive entry points with no
log.audit()
|
| AI SDK logging |
ai
is a dependency
|
| Auth identity |
better-auth
is a dependency
|
| Batching and retry | A production destination was picked — there is nothing to batch otherwise |
| Vite plugin | The framework is Vite-based |
| Condition | Why |
|---|---|
--yes
|
You said so |
--json
|
The payload is the contract; half a TUI in front of it helps nobody |
| stdin or stdout is not a TTY | Piped, redirected, or spawned by a tool |
CI
is set to anything but
false
/
0
|
A workflow runner has no keyboard |
| Id | Destination | Needs |
|---|---|---|
fs
|
Local files under
.evlog/logs
(dev default)
|
nothing |
axiom
|
Axiom |
AXIOM_DATASET
,
AXIOM_API_KEY
|
otlp
|
Any OpenTelemetry collector |
OTEL_EXPORTER_OTLP_ENDPOINT
|
posthog
|
PostHog |
POSTHOG_API_KEY
|
sentry
|
Sentry |
SENTRY_DSN
|
better-stack
|
Better Stack |
BETTER_STACK_API_KEY
|
datadog
|
Datadog |
DATADOG_API_KEY
,
DATADOG_SITE
|
hyperdx
|
HyperDX |
HYPERDX_API_KEY
|
sazabi
|
Sazabi |
SAZABI_INTAKE_URL
|
loki
|
Grafana Loki |
LOKI_ENDPOINT
|
clickhouse
|
ClickHouse |
CLICKHOUSE_ENDPOINT
, and the
evlog_events
table created first
|
none
|
Pretty console output only | nothing |
| Id | What you get |
|---|---|
enrichers
|
Pick from user agent, geo, request size, trace context (
--enrichers
)
|
pipeline
|
Batching and retry instead of one HTTP call per request |
sampling
|
A rate profile, see below (
--sampling
)
|
error-catalog
|
A typed catalog seeded with the errors you already repeat |
audit-catalog
|
Typed audit actions for the sensitive routes with no trail |
ai
|
Token usage, tool calls and cost on AI SDK generations |
better-auth
|
The signed-in user on every event |
vite
|
The Vite plugin that strips
log.debug()
from production builds
|
| Id | Info | Warnings | For |
|---|---|---|---|
all
|
everything | everything | The right answer until volume or cost says otherwise |
low
|
50% | 100% | A small app that has started to repeat itself |
medium
|
25% | 100% | Steady traffic with a bill worth watching |
high
|
10% | 100% | Info is most of what you are paying for |
very-high
|
1% | 50% | Trends rather than individual requests |
| Command | Shows |
|---|---|
evlog logs
|
The last 50 events, oldest first |
evlog logs errors
|
Events that failed: a
5xx
status, an
error
or
fatal
level, or an
error
block
|
evlog logs slow
|
Events over 500ms, worst first (
--over 1s
to move the bar)
|
evlog logs
|
Every event carrying that id, in full: request, error with
why
and
fix
, audit record, then the business fields. The first block of a UUID is enough
|
evlog logs stats
|
The shape of the traffic: per route, count, errors, p50 and p95, routes with the most errors first; then counts by status class and by level |
| Flag | What it does |
|---|---|
--since
|
Only events after: a duration back from now (
15m
,
2h
,
3d
) or a date (
2026-10-01
,
2026-10-01T09:00
)
|
--until
|
Only events before, same spellings |
--level
|
Only these levels:
trace
,
debug
,
info
,
warn
,
error
,
fatal
|
--path
|
Only requests on this exact path |
--status
|
Only this status (
404
) or class (
4xx
)
|
--where
|
Only events where a field matches, see below; repeat the flag for several clauses, all must hold |
--over
|
For
slow
: what a request has to exceed (default
500ms
)
|
--limit
|
Most events to show (default 50) |
--dir
|
Read another directory (default: the project's
.evlog/logs
; in a workspace with no logs at the root, every
apps/*/.evlog/logs
and the like, merged by time with the app in a column)
|
--url
|
Read the
memory drain
's dev endpoint instead of files: a JSON array of events, or
{ "events": [...] }
|
-f
,
--follow
|
Keep reading as the app writes, like
tail -f
;
Ctrl-C
stops
|
--json
|
The events as JSON on stdout |
--cwd
|
Another project in the workspace |
| Clause | Matches when |
|---|---|
user.id=usr_42
|
the field equals the value (
true
/
false
and numbers are read as such)
|
audit.outcome!=success
|
the field differs |
payment.amount>5000
|
greater; also
>=
,
<
,
<=
|
error.message~declined
|
the field matches the regular expression, case-insensitive; an object is matched as JSON |
audit
|
the field is present |
!error
|
the field is absent |
| Area | What it covers |
|---|---|
| API handlers | Server route handlers |
| Pages | Pages that fetch data server-side |
| Middleware & jobs | Middleware, scheduled tasks, server actions |
| Money & auth | Every entry point the sensitivity classifier flagged, wherever it lives |
| Flag | Default | What it does |
|---|---|---|
|
— | Inspect one entry point, by route path or file path |
--all
|
off | Every entry point as a check matrix |
--min-score
|
off |
Exit 1 when the global score is below
n
|
--baseline [ref]
|
off |
Exit 1 on a
regression
against the committed map (path, or
git:
)
|
--baseline-label
|
the ref | What to call the baseline in the report and annotations, for a map copied to a temp file |
--framework
|
detected |
Force
nuxt
,
nitro
,
next
,
tanstack-start
,
hono
,
express
, or
fastify
|
--no-write
|
writes |
Skip writing
evlog.map.json
|
--verbose
|
off | Show per-file parse warnings |
--cwd
|
current | Scan another directory |
--json
|
off |
The full map as JSON on stdout (same as
--format json
)
|
--format
|
human |
github
writes
workflow annotations
to stdout
|
--limit
|
10 |
Most annotations
--format github
emits; GitHub keeps ten per step
|
| Flag | What it does |
|---|---|
--json
|
Machine-readable JSON on stdout instead of the report |
--debug
|
Print a debug summary of the run, and emit it as a wide event |
--noHeader
|
Skip the branded header |
--cwd
|
Run against another directory instead of the current one |
--help
|
Usage for the command |
--version
|
Print the CLI version and exit |
| Code | Meaning |
|---|---|
0
|
The command ran and nothing failed |
1
|
A check failed, or
--min-score
was not met
|
2
|
Usage error — an unknown flag or an invalid value |
| Requirements | Opportunities | |
|---|---|---|
| Effect on the score | Costs points when it fails | None, ever |
| When it appears | Whenever it applies | Only when the project already uses the feature |
| In the report |
FIX FIRST
and
THEN
|
GOING FURTHER
|
| In the JSON |
checks
|
suggestions
|
| Can fail a CI gate | Yes | No |
| Status | Meaning |
|---|---|
pass
|
The rule looked and found what it wanted |
fail
|
The rule looked and did not — costs weight, if it is a requirement |
n/a
|
The question does not make sense here, or you disabled it |
| Column | Id | Weight | Expects |
|---|---|---|---|
log
|
wide-event
|
40 |
useLogger()
|
audit
|
audit
|
25 |
log.audit()
|
err
|
structured-errors
|
20 |
createError({ why, fix })
|
fetch
|
page-error-handling
|
20 | fetch error handling |
ctx
|
context
|
15 |
log.set()
|
catch
|
error-handling
|
15 |
logging or rethrow in
catch
|
| Column | Id | Fires when | Scope |
|---|---|---|---|
catalog
|
error-catalog
|
The project declares a catalog and the same inline error appears in two or more files | Per entry point |
audit+
|
audit-coverage
|
The project records audit events, and this handler changes state without one | Per entry point |
ai
|
ai-logging
|
ai
is a dependency and the AI SDK is called without
evlog/ai
|
Once per project |
identity
|
auth-identity
|
better-auth
is a dependency and
evlog/better-auth
is not installed
|
Once per project |
| Directive | Covers |
|---|---|
// evlog-map-disable-next-line
|
The line below the comment |
// evlog-map-disable-line
|
The line the comment is on, as a trailing comment |
// evlog-map-disable
|
The whole file, wherever it appears |
| Requirement | Weight |
|---|---|
wide-event
|
40 |
audit
|
25 |
structured-errors
|
20 |
page-error-handling
|
20 |
context
|
15 |
error-handling
|
15 |
| Entry point | Weight |
|---|---|
| Flagged money or auth | ×2 |
| Page | ×0.5 |
| Everything else | ×1 |
| Score | Grade |
|---|---|
| 90–100 |
excellent
|
| 70–89 |
good
|
| 50–69 |
needs work
|
| 0–49 |
at risk
|
| Class | When |
|---|---|
instrumented
|
A handler that passes both
wide-event
and
context
, or a page that handles its fetch errors
|
partial
|
A handler that passes one of the two |
dark
|
A handler that passes neither, or a page that swallows fetch errors |
exempt
|
Nothing to instrument — evlog's own infrastructure, or a page that fetches nothing. Counted apart from your own entry points rather than as a gap |
| Recorded | Example |
|---|---|
| Framework, development drain, sampling preset |
nuxt
,
fs
,
balanced
|
| One flag per destination, extra and enricher chosen |
initProdAxiom: true
|
| Counts | files written, manual steps left, doctor failures |
| Whether an offer had anything behind it |
initHadRepeatedErrors: true
|
| Recorded | Example |
|---|---|
| Score and grade |
mapScore: 72
,
mapGrade: good
|
| Entry point counts by coverage |
mapEntryPoints: 34
,
mapDark: 9
|
| Per kind: entry points and dark entry points |
mapKindPage: 12
,
mapDarkPage: 3
|
| Per sensitivity: money / auth / PII entry points, and dark |
mapSensitiveMoney: 2
,
mapDarkMoney: 1
|
| Per rule: how many entry points failed it, how many waived it |
mapFailWideEvent: 6
,
mapSuppressedWideEvent: 2
|
| Which gate ran, and whether it failed |
mapGate: baseline
,
mapGateFailed: true
|
| Command | What it does |
|---|---|
evlog telemetry status
|
Current state, data directory, and the full disclosure |
evlog telemetry enable
|
Enable anonymous usage telemetry |
evlog telemetry disable
|
Disable it and purge undelivered data |
| Variable | Description |
|---|---|
AXIOM_API_KEY
|
Axiom API token with ingest permissions |
AXIOM_DATASET
|
Dataset name to ingest logs into |
AXIOM_ORG_ID
|
Organization ID (required for Personal Access Tokens) |
AXIOM_EDGE_URL
|
Edge base URL for ingest/query (for edge deployments) |
AXIOM_URL
|
API base URL (legacy/default ingest endpoint) |
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
- | Axiom API token (required) |
token
|
string
|
- |
Deprecated.
Use
apiKey
instead
|
dataset
|
string
|
- | Dataset name (required) |
orgId
|
string
|
- | Organization ID (for PAT tokens) |
edgeUrl
|
string
|
- |
Edge URL for ingest. Uses
/v1/ingest/{dataset}
when no path is provided; custom paths are used as-is (trailing slash trimmed). Mutually exclusive with
baseUrl
|
baseUrl
|
string
|
https://api.axiom.co
|
API base URL (
/v1/datasets/{dataset}/ingest
), mutually exclusive with
edgeUrl
|
timeout
|
number
|
5000
|
Request timeout in milliseconds |
retries
|
number
|
2
|
Number of retry attempts on transient failures |
| Variable | Description |
|---|---|
BETTER_STACK_API_KEY
|
Better Stack source token (required) |
BETTER_STACK_ENDPOINT
|
Custom ingestion endpoint |
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
- | Better Stack source token (required) |
sourceToken
|
string
|
- |
Deprecated.
Use
apiKey
instead
|
endpoint
|
string
|
https://in.logs.betterstack.com
|
Ingestion endpoint |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
| Variable | Description |
|---|---|
DD_API_KEY
|
Datadog API key (required). Also:
DATADOG_API_KEY
|
DD_SITE
|
Site hostname (e.g.
datadoghq.com
,
datadoghq.eu
,
us3.datadoghq.com
). Also:
DATADOG_SITE
|
DATADOG_LOGS_URL
|
Full intake URL — overrides URL derived from
site
|
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
— | Datadog API key (required) |
site
|
string
|
datadoghq.com
|
Site for intake host
http-intake.logs.${site}
|
intakeUrl
|
string
|
from
site
|
Full
POST
URL for
/api/v2/logs
|
timeout
|
number
|
5000
|
Request timeout (ms) |
retries
|
number
|
2
|
Retries on transient failures |
| Variable | Description |
|---|---|
POSTHOG_API_KEY
|
Project API key (starts with
phc_
)
|
POSTHOG_HOST
|
PostHog host URL (for EU or self-hosted) |
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
- | Project API key (required) |
host
|
string
|
https://us.i.posthog.com
|
PostHog host URL |
distinctId
|
string
|
- | Static person identifier for every event |
distinctIdField
|
string
|
userId
|
Event field holding the person identifier (dot path) |
sessionIdField
|
string
|
sessionId
|
Event field holding the PostHog session id (dot path) |
recordShape
|
'json' | 'compact'
|
'json'
|
Log record shape, see below |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
retries
|
number
|
2
|
Retry attempts on transient failures |
| Region | Host |
|---|---|
| US (default) |
https://us.i.posthog.com
|
| EU |
https://eu.i.posthog.com
|
| Self-hosted | Your instance URL |
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
- | Project API key (required) |
host
|
string
|
https://us.i.posthog.com
|
PostHog host URL |
eventName
|
string
|
evlog_wide_event
|
PostHog event name |
distinctId
|
string
|
- |
Static
distinct_id
for all events
|
distinctIdField
|
string
|
userId
|
Event field holding the
distinct_id
(dot path)
|
recordShape
|
'json' | 'compact'
|
'json'
|
Event property shape:
compact
flattens nested fields (see
below
)
|
timeout
|
number
|
5000
|
Request timeout in milliseconds |
| evlog Field | PostHog Field |
|---|---|
config.distinctId
or
userId
or
service
|
distinct_id
(fallback chain)
|
timestamp
|
timestamp
|
level
|
properties.level
|
service
|
properties.service
|
environment
|
properties.environment
|
| All other fields |
properties.*
|
createPostHogDrain()
|
createPostHogDrain({ mode: 'events' })
|
|
|---|---|---|
| Format |
OTLP Logs (
/i/v1/logs
)
|
PostHog Events (
/batch/
)
|
| PostHog UI | Logs viewer | Events explorer |
| Cost | Lower (dedicated logs pipeline) | Higher (counts as events) |
| Best for | Debugging, log search, observability | Product analytics, cohorts, funnels |
| Variable | Description |
|---|---|
SAZABI_INTAKE_URL
|
Intake URL of the log stream. evlog posts to
; a URL that already ends with
/v1/logs
is used as is
|
SAZABI_SERVICE_NAME
|
Override
service.name
|
OTEL_SERVICE_NAME
|
Fallback for
service.name
(same as the OTLP adapter)
|
| Option | Type | Default | Description |
|---|---|---|---|
endpoint
|
string
|
SAZABI_INTAKE_URL
|
Intake URL of the log stream |
serviceName
|
string
|
event.service
|
Override
service.name
|
resourceAttributes
|
object
|
- | Additional OTLP resource attributes |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
retries
|
number
|
2
|
Retry attempts on transient failures |
recordShape
|
'compact' | 'json'
|
'compact'
|
Log record shape: a one-line summary body with dotted attributes, or the serialized event as the body |
| Variable | Description |
|---|---|
SENTRY_DSN
|
Sentry DSN (required) |
SENTRY_ENVIRONMENT
|
Environment name override |
SENTRY_RELEASE
|
Release version override |
| Option | Type | Default | Description |
|---|---|---|---|
dsn
|
string
|
- | Sentry DSN (required) |
environment
|
string
|
Event environment | Environment name |
release
|
string
|
Event version | Release version |
tags
|
Record
|
- | Additional attributes to attach |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
issues
|
boolean
|
false
|
Also send
error
and
fatal
events as Sentry error events
|
| Variable | Required | Description |
|---|---|---|
CLICKHOUSE_ENDPOINT
|
Yes |
HTTP interface URL (
CLICKHOUSE_URL
also accepted)
|
CLICKHOUSE_USER
|
No |
Username. Default
default
|
CLICKHOUSE_PASSWORD
|
No | Password. Omit for an unauthenticated local instance |
CLICKHOUSE_DATABASE
|
No |
Database. Default
default
|
CLICKHOUSE_TABLE
|
No |
Table. Default
evlog_events
|
| Option | Type | Default | Description |
|---|---|---|---|
endpoint
|
string
|
— | HTTP interface URL |
database
|
string
|
default
|
Target database |
table
|
string
|
evlog_events
|
Target table |
username
|
string
|
default
|
Username |
password
|
string
|
— | Password |
asyncInsert
|
boolean
|
true
|
Batch inserts server-side |
waitForAsyncInsert
|
boolean
|
false
|
Wait for the flush before responding |
transform
|
(event) => Record
|
toClickHouseRow
|
Map an event to a row |
timeout
|
number
|
5000
|
Request timeout in ms |
retries
|
number
|
2
|
Retry attempts |
| Variable | Description |
|---|---|
HYPERDX_API_KEY
|
Ingestion API key (sent as the
authorization
header)
|
HYPERDX_OTLP_ENDPOINT
|
OTLP HTTP base URL (default:
https://in-otel.hyperdx.io
)
|
HYPERDX_SERVICE_NAME
|
Override
service.name
|
| Variable | Description |
|---|---|
OTEL_SERVICE_NAME
|
Fallback for service name (HyperDX SDK examples use this) |
| Option | Type | Default | Description |
|---|---|---|---|
apiKey
|
string
|
- |
Ingestion API key (required). Sent as the
authorization
header value
|
endpoint
|
string
|
https://in-otel.hyperdx.io
|
OTLP HTTP base URL (evlog appends
/v1/logs
)
|
serviceName
|
string
|
- |
Override
service.name
resource attribute
|
resourceAttributes
|
object
|
- | Additional OTLP resource attributes |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
retries
|
number
|
2
|
Retry attempts on transient failures |
| Variable | Required | Description |
|---|---|---|
LOKI_ENDPOINT
|
Yes |
Base URL of the Loki instance, without the push path (
LOKI_URL
also accepted)
|
LOKI_API_KEY
|
No |
API token. Sent as Basic with
LOKI_USER
, otherwise as Bearer (
GRAFANA_API_KEY
also accepted)
|
LOKI_USER
|
No |
Grafana Cloud instance ID. Switches auth to Basic (
GRAFANA_USER
also accepted)
|
LOKI_TENANT_ID
|
No |
Tenant for multi-tenant self-hosted Loki, sent as
X-Scope-OrgID
|
| Option | Type | Default | Description |
|---|---|---|---|
endpoint
|
string
|
— |
Base URL, without
/loki/api/v1/push
|
apiKey
|
string
|
— | API token |
user
|
string
|
— | Grafana Cloud instance ID (enables Basic auth) |
tenantId
|
string
|
— |
X-Scope-OrgID
for multi-tenant Loki
|
labelFields
|
string[]
|
['service', 'environment', 'level']
|
Event fields promoted to Loki labels |
labels
|
Record
|
— | Static labels merged into every stream |
timeout
|
number
|
5000
|
Request timeout in ms |
retries
|
number
|
2
|
Retry attempts on transient failures |
user
|
apiKey
|
tenantId
|
Header sent |
|---|---|---|---|
| ✓ | ✓ |
Authorization: Basic base64(user:apiKey)
|
|
| ✓ |
Authorization: Bearer
|
||
| ✓ |
X-Scope-OrgID:
|
||
| none — unauthenticated instance |
| Variable | Description |
|---|---|
OTLP_ENDPOINT
|
OTLP HTTP endpoint (e.g.,
http://localhost:4318
). The standard
OTEL_EXPORTER_OTLP_ENDPOINT
also works.
|
OTLP_HEADERS
|
Headers as
key=value
pairs, comma-separated. The standard
OTEL_EXPORTER_OTLP_HEADERS
also works.
|
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
|
Full logs URL, used as-is with no
/v1/logs
appended. Takes precedence over
OTEL_EXPORTER_OTLP_ENDPOINT
.
|
OTEL_EXPORTER_OTLP_LOGS_HEADERS
|
Headers for the logs signal. Merged over
OTEL_EXPORTER_OTLP_HEADERS
, winning on conflicts.
|
OTEL_EXPORTER_OTLP_COMPRESSION
|
gzip
or
none
.
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION
takes precedence.
|
OTEL_EXPORTER_OTLP_PROTOCOL
|
http/json
or
http/protobuf
.
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL
takes precedence.
grpc
is not supported.
|
OTEL_SERVICE_NAME
|
Service name override |
OTEL_RESOURCE_ATTRIBUTES
|
Resource attributes as
key=value
pairs, comma-separated.
resourceAttributes
and the event's
service
,
environment
,
version
,
region
and
commitHash
take precedence.
|
| Option | Type | Default | Description |
|---|---|---|---|
endpoint
|
string
|
- | OTLP HTTP endpoint (required) |
serviceName
|
string
|
From event |
Override
service.name
resource attribute
|
headers
|
object
|
- | Custom HTTP headers for authentication |
resourceAttributes
|
object
|
- | Additional OTLP resource attributes |
recordShape
|
'json' | 'compact'
|
'json'
|
How the record carries the event ( details ) |
compression
|
'gzip' | 'none'
|
'none'
|
Gzip the request body and send
Content-Encoding: gzip
|
protocol
|
'http/json' | 'http/protobuf'
|
'http/json'
|
Request body encoding ( details ) |
semanticConventions
|
boolean
|
false
|
Add OpenTelemetry attribute names next to the evlog ones ( details ) |
timeout
|
number
|
5000
|
Request timeout in milliseconds |
| evlog Field | OTLP Field |
|---|---|
level
|
severityNumber
/
severityText
|
timestamp
|
timeUnixNano
|
service
|
Resource attribute
service.name
|
environment
|
Resource attribute
deployment.environment
, plus
deployment.environment.name
with
semanticConventions
|
version
|
Resource attribute
service.version
|
region
|
Resource attribute
cloud.region
|
traceId
|
traceId
|
spanId
|
spanId
|
| All other fields | Log attributes |
| evlog field | Attribute |
|---|---|
method
|
http.request.method
|
path
|
url.path
|
status
|
http.response.status_code
|
userAgent.raw
|
user_agent.original
|
error.name
/
error.message
/
error.stack
|
exception.type
/
exception.message
/
exception.stacktrace
|
ai.model
/
ai.provider
/
ai.responseId
|
gen_ai.request.model
/
gen_ai.provider.name
/
gen_ai.response.id
|
ai.inputTokens
/
ai.outputTokens
|
gen_ai.usage.input_tokens
/
gen_ai.usage.output_tokens
|
ai.cacheReadTokens
/
ai.cacheWriteTokens
|
gen_ai.usage.cache_read.input_tokens
/
gen_ai.usage.cache_creation.input_tokens
|
ai.finishReason
|
gen_ai.response.finish_reasons
|
| evlog Level | OTLP Severity Number | OTLP Severity Text |
|---|---|---|
trace
|
1 | TRACE |
debug
|
5 | DEBUG |
info
|
9 | INFO |
warn
|
13 | WARN |
error
|
17 | ERROR |
fatal
|
21 | FATAL |
| Field | Type | Description |
|---|---|---|
event
|
WideEvent
|
The complete log event with all accumulated context |
request
|
object
|
Request metadata (
method
,
path
,
requestId
)
|
headers
|
object
|
Safe HTTP headers (sensitive headers are filtered) |
| API | Missing credentials |
|---|---|
create*Drain()
(factory)
|
console.error
+ drain becomes a no-op — the HTTP response is never blocked
|
sendTo*
/
sendBatchTo*
(direct)
|
Throws if required credentials are absent — caller must pass a complete config |
| Option | Type | Default | Description |
|---|---|---|---|
dir
|
string
|
'.evlog/logs'
|
Directory for log files |
maxFiles
|
number
|
undefined
|
Max files to keep (auto-deletes oldest) |
maxSizePerFile
|
number
|
undefined
|
Max bytes per file before rotating |
pretty
|
boolean
|
false
|
Pretty-print JSON (multi-line, readable) |
| Option | Type | Default | Description |
|---|---|---|---|
maxEvents
|
number
|
1000
|
Maximum events to keep in the ring buffer (oldest are dropped) |
store
|
string
|
'default'
|
Named buffer key — multiple drains sharing the same key share the same buffer |
| Variable | Description |
|---|---|
EVLOG_MEMORY_STORE
|
Named buffer key (default:
'default'
)
|
EVLOG_MEMORY_MAX_EVENTS
|
Ring buffer size (default:
1000
)
|
| Option | Type | Description |
|---|---|---|
store
|
string
|
Named store to read from (default:
'default'
)
|
since
|
Date | string
|
Only events with
timestamp >= since
|
until
|
Date | string
|
Only events with
timestamp <= until
|
level
|
LogLevel | LogLevel[]
|
Filter by level |
filter
|
(event) => boolean
|
Custom predicate |
limit
|
number
|
Return at most N most-recent matching events |
| Query param |
Type in
ReadMemoryLogsOptions
|
Notes |
|---|---|---|
store
|
string
|
Passed through as-is |
since
|
string
|
ISO 8601 string — parsed by
readMemoryLogs
|
until
|
string
|
ISO 8601 string — parsed by
readMemoryLogs
|
level
|
LogLevel | LogLevel[]
|
Comma-separated (
error,warn
) or repeated array; invalid values are dropped
|
limit
|
number
|
parseInt
; NaN → omitted
|
| Column | Type | Description |
|---|---|---|
id
|
text
|
UUID primary key |
timestamp
|
text
|
Event timestamp |
level
|
text
|
Log level (info, warn, error, debug) |
service
|
text
|
Service name |
environment
|
text
|
Environment (production, staging, etc.) |
method
|
text
|
HTTP method |
path
|
text
|
Request path |
status
|
integer
|
HTTP status code |
duration_ms
|
integer
|
Request duration in milliseconds |
request_id
|
text
|
Request correlation ID |
source
|
text
|
Event source (server, client) |
error
|
text
|
Error details (JSON string) |
data
|
text
|
All remaining event fields (JSON) |
created_at
|
text
|
Row insertion timestamp |
| Unit | Description | Example |
|---|---|---|
d
|
Days |
7d
= 7 days
|
h
|
Hours |
24h
= 24 hours
|
m
|
Minutes |
60m
= 60 minutes
|
| Retention | Cron Schedule | Description |
|---|---|---|
60m
|
*/30 * * * *
|
Every 30 minutes |
24h
|
0 */12 * * *
|
Every 12 hours |
7d
|
0 3 * * *
|
Daily at 3:00 AM |
30d
|
0 3 * * *
|
Daily at 3:00 AM |
| Option | Type | Effect on the event |
|---|---|---|
fields
|
Record
|
Fields the event starts with |
traceparent
|
string
|
Continue this W3C trace instead of the current span. A malformed value starts a new trace |
tracestate
|
string
|
Forwarded on outgoing requests next to
traceparent
|
| Option | Type | Default | Description |
|---|---|---|---|
captureOutput
|
boolean | CaptureOutputOptions
|
false
|
Capture stdout/stderr as structured log events |
| Field | Type | Default | Description |
|---|---|---|---|
stdout
|
boolean
|
true
|
Capture stdout writes |
stderr
|
boolean
|
true
|
Capture stderr writes |
ignore
|
(string | RegExp)[]
|
Next.js Edge bundler warnings | Skip re-emitting matching chunks as log events |
| Option | Type | Default | Description |
|---|---|---|---|
service
|
string
|
'app'
|
Service name shown in logs |
environment
|
string
|
Auto-detected | Environment name |
include
|
string[]
|
undefined
|
Route patterns to log |
exclude
|
string[]
|
undefined
|
Route patterns to exclude |
routes
|
Record
|
undefined
|
Route-specific service configuration |
sampling.rates
|
object
|
undefined
|
Head sampling rates per log level |
sampling.keep
|
array
|
undefined
|
Tail sampling conditions |
keep
|
(ctx: TailSamplingContext) => void
|
undefined
|
Custom tail sampling callback |
drain
|
DrainFunction
|
undefined
|
Drain adapter for external services |
enrich
|
(ctx: EnrichContext) => void
|
undefined
|
Event enrichment callback |
| Option | Type | Default | Description |
|---|---|---|---|
enabled
|
boolean
|
true
|
Globally enable/disable all logging. When
false
, all operations become no-ops
|
console
|
boolean
|
true
|
Enable/disable browser console output |
env.service
|
string
|
'app'
|
Service name shown in logs |
env.environment
|
string
|
Auto-detected | Environment name |
include
|
string[]
|
undefined
|
Route patterns to log. Supports glob (
/api/**
)
|
exclude
|
string[]
|
undefined
|
Route patterns to exclude. Exclusions take precedence |
routes
|
Record
|
undefined
|
Route-specific service configuration |
pretty
|
boolean
|
true
in dev
|
Pretty print with tree formatting |
dev
|
'evlog' | 'nitro' | 'both' | object
|
'evlog'
in pretty dev
|
Dev terminal presets or
{ frameworkOverlay, prettyError }
— see
Configuration — Dev terminal output
|
| Framework | Import | Type | Logger Access | Status |
|---|---|---|---|---|
| Nuxt |
evlog/nuxt
|
Module |
useLogger(event)
|
Stable |
| Next.js |
evlog/next
|
Factory |
useLogger()
|
Stable |
| SvelteKit |
evlog/sveltekit
|
Hooks |
event.locals.log
/
useLogger()
|
Stable |
| Nitro |
evlog/nitro
|
Module |
useLogger(event)
|
Stable |
| TanStack Start |
evlog/nitro/v3
|
Module |
useRequest().context.log
|
Stable |
| TanStack Router |
evlog/nitro/v3
|
Module | Via TanStack Start (uses Nitro v3) | Stable |
| React Router |
evlog/react-router
|
Middleware |
context.get(loggerContext)
/
useLogger()
|
Stable |
| NestJS |
evlog/nestjs
|
Module |
useLogger()
|
Stable |
| Express |
evlog/express
|
Middleware |
req.log
/
useLogger()
|
Stable |
| Hono |
evlog/hono
|
Middleware |
c.get('log')
/
useLogger()
|
Stable |
| Fastify |
evlog/fastify
|
Plugin |
request.log
/
useLogger()
|
Stable |
| Elysia |
evlog/elysia
|
Plugin |
log
(context) /
useLogger()
|
Stable |
| oRPC |
evlog/orpc
|
Handler wrapper + middleware |
context.log
/
useLogger()
|
Stable |
| Effect |
evlog/effect
|
Layer |
yield* EvlogLogger
/
yield* useLogger
|
Beta |
| Cloudflare Workers |
evlog/workers
|
Factory |
createWorkersLogger()
|
Stable |
| AWS Lambda |
evlog
|
Manual |
createLogger()
/
createRequestLogger()
|
Guide |
| Standalone |
evlog
|
Manual |
createLogger()
/
createRequestLogger()
|
Stable |
| Astro |
evlog
|
Manual |
createRequestLogger()
|
Guide |
| Custom |
evlog/toolkit
|
Build your own |
createMiddlewareLogger()
|
Beta |
| Pattern | Frameworks |
|---|---|
evlog(options)
middleware / plugin
|
Hono, Express, Fastify, Elysia, SvelteKit, React Router |
createEvlog(options)
factory
|
Next.js |
EvlogModule.forRoot()
|
NestJS |
routerLayer(options)
layer
|
Effect |
| Module default export | Nuxt, Nitro v2/v3 |
| Manual factory |
Cloudflare Workers (
createWorkersLogger
), Standalone, AWS Lambda, Astro
|
| Pattern | Frameworks |
|---|---|
useLogger(event)
|
Nuxt, Nitro |
useLogger()
|
Next.js, NestJS, Express, Fastify, Elysia, SvelteKit, React Router, Hono |
c.get('log')
|
Hono |
req.log
|
Express |
request.log
|
Fastify |
event.locals.log
|
SvelteKit |
context.get(loggerContext)
|
React Router |
yield* EvlogLogger
/
yield* useLogger
|
Effect |
createRequestLogger()
/
createLogger()
|
Standalone, Workers, manual setups |
| Wide event field | Source | Description |
|---|---|---|
ai.calls
|
Call count | Number of AI calls in this request |
ai.model
|
response.modelId
|
Model that served the response |
ai.models
|
All model IDs | Array of all models used (only when > 1) |
ai.provider
|
model.provider
|
Provider (
anthropic
,
openai
,
google
, etc.)
|
ai.inputTokens
|
usage.inputTokens.total
|
Total input tokens across all calls |
ai.outputTokens
|
usage.outputTokens.total
|
Total output tokens across all calls |
ai.totalTokens
|
Computed |
inputTokens + outputTokens
|
ai.cacheReadTokens
|
usage.inputTokens.cacheRead
|
Tokens served from prompt cache |
ai.cacheWriteTokens
|
usage.inputTokens.cacheWrite
|
Tokens written to prompt cache |
ai.reasoningTokens
|
usage.outputTokens.reasoning
|
Reasoning tokens (extended thinking) |
ai.finishReason
|
finishReason.unified
|
Why generation ended (
stop
,
tool-calls
, etc.)
|
ai.toolCalls
|
Content / stream chunks |
string[]
of tool names by default, or
Array<{ name, input }>
when
toolInputs
is enabled
|
ai.responseId
|
response.id
|
Provider-assigned response ID (e.g. Anthropic's
msg_...
)
|
ai.prompt
|
First call's
params.prompt
|
Formatted text of the prompt sent to the first model call (only when the
prompt
option is enabled)
|
ai.output
|
Content / stream chunks |
Text generated by the last model call (only when the
output
option is enabled)
|
ai.steps
|
Step count | Number of LLM calls (only when > 1) |
ai.stepsUsage
|
Per-step accumulation | Per-step token and tool call breakdown (only when > 1 step) |
ai.msToFirstChunk
|
Stream timing | Time to first text chunk (streaming only) |
ai.msToFinish
|
Stream timing | Total stream duration (streaming only) |
ai.tokensPerSecond
|
Computed | Output tokens per second (streaming only) |
ai.error
|
Error capture | Error message if a model call fails |
ai.tools
|
Telemetry integration |
Per-tool
{ name, durationMs, success, error? }
(requires
createEvlogIntegration
)
|
ai.totalDurationMs
|
Telemetry integration |
Total generation wall time (requires
createEvlogIntegration
)
|
ai.embedding
|
captureEmbed
|
{ model?, tokens, dimensions?, count? }
— embedding metadata
|
ai.estimatedCost
|
Computed |
Estimated cost in dollars (requires
cost
option)
|
| Option | Type | Default | Description |
|---|---|---|---|
toolInputs
|
boolean | ToolInputsOptions
|
false
|
Capture tool call inputs alongside their names (off by default to avoid leaking sensitive data). |
prompt
|
boolean | CaptureOptions
|
false
|
Capture the prompt sent to the first model call as
ai.prompt
(off by default to avoid leaking sensitive data).
|
output
|
boolean | CaptureOptions
|
false
|
Capture the text generated by the last model call as
ai.output
(off by default to avoid leaking sensitive data).
|
cost
|
Record
|
undefined
|
Pricing map. Keys are model IDs, values are
{ input, output }
in dollars per 1M tokens.
|
| Sub-option | Type | Description |
|---|---|---|
maxLength
|
number
|
Truncate stringified inputs exceeding this character length (appends
…
).
|
transform
|
(input, toolName) => unknown
|
Custom transform applied before
maxLength
. Use to redact fields or reshape data.
|
| Sub-option | Type | Description |
|---|---|---|
maxLength
|
number
|
Truncate captured text exceeding this character length (appends
…
).
|
transform
|
(content) => string
|
Custom transform applied before
maxLength
.
|
| Method | Description |
|---|---|
wrap(model)
|
Wraps a language model with middleware. Accepts a model string (e.g.
'anthropic/claude-sonnet-4.6'
) or a
LanguageModelV3
/
LanguageModelV4
object. Works with
generateText
,
streamText
, and
ToolLoopAgent
.
|
captureEmbed(result)
|
Manually captures token usage, model info, and dimensions from
embed()
or
embedMany()
results.
|
getMetadata()
|
Returns a snapshot of the current execution metadata. See Access Metadata . |
getEstimatedCost()
|
Returns the current estimated cost in dollars when a
cost
map is configured.
|
onUpdate(callback)
|
Subscribe to metadata updates. Fires on every step, embed, error, and integration finish. |
| Data | Source | Description |
|---|---|---|
ai.tools[]
|
onToolExecutionEnd
(v7) /
onToolCallFinish
(v6)
|
Per-tool
name
,
durationMs
,
success
, and
error
(if failed)
|
ai.totalDurationMs
|
onStart
→
onEnd
(v7) /
onFinish
(v6)
|
Total wall time from generation start to completion |
ai.embedding
|
onEmbedEnd
(v7)
|
Auto-captured from
embed()
/
embedMany()
when telemetry is enabled
|
ai.finishReason: 'abort'
|
onAbort
(v7)
|
Set when a streaming generation is aborted |
ai.error
|
onAbort
/
onError
(v7)
|
Abort reason or unrecoverable generation error |
| Drain | Retention mechanism |
|---|---|
| FS |
Combine
createFsDrain({ maxFiles })
with a daily compactor.
|
| Postgres |
Schedule
DELETE FROM audit_events WHERE timestamp < now() - interval '7 years'
.
|
| Axiom / Datadog / Loki | Set the dataset retention policy in the platform. |
| S3 Object Lock | Configure lifecycle rules + Object Lock retention period. |
| Strategy | What it adds | Use case |
|---|---|---|
'hmac'
|
event.audit.signature
(HMAC of the canonical event)
|
Single-event integrity check (any later mutation fails verification). |
'hash-chain'
|
event.audit.prevHash
and
event.audit.hash
|
A verifiable chain — deletions and reordering also become detectable. |
| Symbol | Kind | Notes |
|---|---|---|
AuditFields
|
type | Reserved field on the wide event |
defineAuditAction(name, opts?)
|
factory | Typed action registry, infers target shape |
defineAuditCatalog(prefix, map)
|
factory | Bundle of typed audit actions sharing a prefix |
log.audit(fields)
|
method |
Sugar over
log.set({ audit })
+ force-keep
|
log.audit.deny(reason, fields)
|
method | Records a denied action |
audit(fields)
|
function | Standalone for scripts / jobs |
withAudit({ action, target })(fn)
|
wrapper | Auto-emit success / failure / denied |
auditDiff(before, after)
|
helper |
Redact-aware JSON Patch for
changes
|
mockAudit()
|
test util |
Capture audits on emit;
assertAudit()
or
toIncludeAuditOf()
|
auditEnricher(opts?)
|
enricher | Auto-fill request / runtime / tenant context |
auditOnly(drain, { await? })
|
wrapper |
Routes only events with an
audit
field
|
signed(drain, opts)
|
wrapper | Generic integrity wrapper (hmac / hash-chain) |
auditRedactPreset
|
config | Strict PII for audit events |
| Entry field | Purpose |
|---|---|
target
|
Default
target.type
injected at call sites
|
description
|
Human-readable label for docs, SIEM rules, review tooling |
severity
|
'low' | 'medium' | 'high' | 'critical'
— alerting and review priority
|
requiresChanges
|
Document that callers should attach
changes
(e.g. via
auditDiff
)
|
requiresReason
|
Document that callers should attach
reason
(especially denials)
|
redactPaths
|
Default paths for
auditDiff({ redactPaths: [...] })
on this action
|
actor.type
|
When to use |
|---|---|
'user'
|
A human authenticated through your normal auth flow. |
'system'
|
Cron jobs, queue workers, scheduled tasks, internal background processes. |
'api'
|
Machine-to-machine calls from another service authenticated by a token. |
'agent'
|
AI tool calls (combine with
evlog/ai
fields like
model
,
tools
,
promptId
).
|
outcome
|
Meaning |
|---|---|
'success'
|
The action completed as requested. |
'failure'
|
The action was attempted but failed (downstream error, race condition, etc.). |
'denied'
|
The action was rejected by an authorisation check. |
| Field | Use case |
|---|---|
correlationId
|
Shared by every audit event that belongs to the same operation (e.g. one HTTP request that triggers a refund + an email + a webhook). |
causationId
|
The id of the previous audit event that caused this one. Useful for reconstructing chains of cascading actions. |
| Option | Type | Default | Description |
|---|---|---|---|
maskEmail
|
boolean
|
false
|
Mask emails as
h***@example.com
.
|
session
|
boolean
|
true
|
Include session metadata (
session.id
,
session.expiresAt
,
session.ipAddress
,
session.userAgent
).
|
fields
|
string[]
|
['id', 'name', 'email', 'image', 'emailVerified', 'createdAt']
|
User fields to extract. |
extend
|
(session) => Record
|
undefined
|
Add custom fields from Better Auth plugins (organizations, roles, etc.). |
| Field | Source | Description |
|---|---|---|
userId
|
session.user.id
|
Top-level user ID (used by PostHog adapter as
distinct_id
).
|
user.id
|
session.user.id
|
User ID. |
user.name
|
session.user.name
|
Display name. |
user.email
|
session.user.email
|
Email (maskable with
maskEmail: true
).
|
user.image
|
session.user.image
|
Avatar URL. |
user.emailVerified
|
session.user.emailVerified
|
Email verification status. |
user.createdAt
|
session.user.createdAt
|
Account creation date (ISO string). |
session.id
|
session.session.id
|
Session ID. |
session.expiresAt
|
session.session.expiresAt
|
Session expiry (ISO string). |
session.ipAddress
|
session.session.ipAddress
|
Client IP from the session. |
session.userAgent
|
session.session.userAgent
|
User agent string from the session. |
session.createdAt
|
session.session.createdAt
|
Session creation date (ISO string). |
auth.resolvedIn
|
Measured | Session resolution time in ms. |
auth.identified
|
Computed | Whether the request was identified. |
| Option | Type | Default | Description |
|---|---|---|---|
exclude
|
string[]
|
['/api/auth/**']
|
Route patterns to skip (glob). |
include
|
string[]
|
undefined
|
If set, only matching routes are resolved. |
onIdentify
|
(log, session) => void
|
undefined
|
Called after successful identification. |
onAnonymous
|
(log) => void
|
undefined
|
Called when no session is found. |
| Export | Description |
|---|---|
identifyUser(log, session)
|
Core helper — extracts safe fields from a session and sets them on the logger. Returns
true
if identified.
|
createAuthMiddleware(auth)
|
Returns an async
(log, headers, path?) => Promise
function with route filtering, timing, and hooks.
|
createAuthIdentifier(auth)
|
Nitro
request
hook factory for standalone Nitro apps. See
Performance
.
|
maskEmail(email)
|
Mask an email:
hugo@example.com
→
h***@example.com
.
|
| Option | Default | Description |
|---|---|---|
enabled
|
true
|
Enable or disable all client logging |
console
|
true
|
Output logs to the browser console |
pretty
|
true
|
Use colored, formatted console output |
minLevel
|
'debug'
|
Minimum severity:
trace
<
debug
<
info
<
warn
<
error
<
fatal
|
service
|
'client'
|
Service name included in every log event |
transport
|
- | Send logs to a server endpoint (see below) |
| Platform | Headers | Coverage |
|---|---|---|
| Vercel |
x-vercel-ip-country
,
x-vercel-ip-country-region
,
x-vercel-ip-city
,
x-vercel-ip-latitude
,
x-vercel-ip-longitude
|
Full |
| Cloudflare |
cf-ipcountry
|
Country only |
| Need | Use |
|---|---|
| Debug a session in Vercel | eve Agent Runs (automatic) |
| Span-level traces in Datadog / Honeycomb |
eve
agent/instrumentation.ts
+ OTel exporter
|
| Wide events to Axiom / Better Stack / FS, billing, audit, tail sampling |
evlog/eve
|
| Jumping from a span to its wide event, and back |
defineEvlogInstrumentation()
(
below
)
|
| Field | What it tells you |
|---|---|
eve.runtime
|
eve version, agent id, model, and the deployed
gitSha
/
gitBranch
/
deployedAt
|
eve.caller
|
Who triggered the turn:
principalId
,
principalType
and
authenticator
. On a multi-user channel this is what you group cost and volume by.
subject
and
attributes
are never recorded (a channel may put a name or an email in them)
|
eve.parent
|
Parent and root session ids for a subagent run: rebuild the delegation tree with
rootSessionId
|
eve.authorizations
|
Connection sign-ins with their
outcome
,
reason
and duration
|
eve.compaction
|
How many compactions ran, on which model, and
inputTokensAtTrigger
(how full the context was when the first one fired)
|
eve.stepFailures
/
eve.failedSteps
|
Model calls that failed, including on a turn that then succeeded on retry |
eve.subagents
|
Subagent and remote agent calls with their name, status (
called
,
completed
or
failed
), duration and error message.
remote: true
marks a remote agent
|
eve.reasoning
|
blocks
and
chars
, the size of the model's thinking, never its content
|
eve.result
|
The structured result, for an agent with an output schema |
eve.contextCleared
|
The durable history was wiped during this turn |
message.responseChars
|
Length of the agent's response, recorded whatever the
message
mode
|
ai.costUsd
|
Cost as reported by eve.
ai.estimatedCost
is the fallback computed from
cost
|
ai.model
/
ai.provider
|
The gateway slug (
deepseek/deepseek-v4-flash
) and the resolved provider (
deepseek
) that served the turn. The model is the one eve reports on the turn's last
step.started
, so a gateway fallback shows up, then the
model
option, then the model eve reports on
session.started
.
ai.provider
is split from that model id
|
ai.tools[].inputTokens
|
The input tokens a tool result added to the next model call: the delta between the step that called the tool and the step that consumed its result. Parallel results in one step share the delta evenly; left unset when the turn ended before the result was consumed, or when a compaction reset the context |
| Concern | Recommendation |
|---|---|
| Terminal output |
pretty: false
, since pretty-print is for local dev only
|
| Drain latency | Batch or async HTTP drains; never block the turn on I/O |
| Head sampling |
sampling.rates
, since eve emits one event per turn, not per token
|
| Memory |
maxSessions
(default
256
) evicts oldest idle session state
|
| Load | Hook handlers are O(1) per stream event; cost is dominated by your drain |
| Option | Description |
|---|---|
service
/
environment
/
sampling
/
silent
/
redact
|
Logger settings, applied on the first hook invocation.
service
defaults to
eve-agent
|
drain
/
enrich
/
keep
/
plugins
|
Same as HTTP integrations (
plugins
).
drain
and
plugins
also apply to events emitted outside a turn
|
init
|
Passed to
initLogger()
as is, in place of the logger settings. Use it to give events emitted outside a turn their own drain or service
|
enrichTurn
|
Turn-scoped enrichment with the eve session in scope (
ctx.session.auth
); runs once per turn, returned fields merge onto the turn event
|
message
|
'omit'
(default),
'preview'
or
'full'
(how much of the user message and the agent response to record)
|
messagePreviewLength
|
Characters kept in
'preview'
mode (default
500
)
|
sessionEvent
|
true
emits one extra event per session, rolling up its turns
|
subagentEvents
|
false
skips subagent lifecycle subscriptions when their event source has no hook session context
|
cost
/
model
|
Fallback token pricing (
ModelCost
from
evlog/ai
) →
ai.estimatedCost
, used only when eve reports no cost
|
maxSessions
|
In-memory session cap for context carry-over (default
256
)
|
include
/
exclude
|
Route-style filters on turn paths (
/sessions/*/turns/*
)
|
| You want to… | See |
|---|---|
| Follow a Node.js request from response ID to a JSON event, including a failed payment | Node.js request logging |
Send browser logs to your server with batching, retries, and
sendBeacon
fallback
|
Client Logging |
| Capture every AI SDK call with token usage, tool calls, streaming metrics, and cost | AI SDK |
| Identify the authenticated user (and their org / role) on every wide event | Better Auth |
| Build a tamper-evident audit trail with hash chains, denials, redaction-aware diffs | Audit Logs |
| Instrument CLIs and automation with one wide event per run, consent, and disclosure | Telemetry |
| Ask a decision model one question per event and keep the 200s that were silent failures | Signals |
| Export wide events from eve agent turns (tokens, tools, drains) | eve |
| Add derived context (User-Agent, geo, request size, trace context) to every event | Enrichers |
| You want to | Write | You get | Do this with it |
|---|---|---|---|
| Flag events |
ask
|
value: boolean
,
confidence
|
WHERE signals.x.value = true
, or
keep
the event
|
| Categorise events |
ask
+
choice
|
value
is one option name,
confidence
when the model gives one
|
GROUP BY signals.x.value
|
| Rank events |
ask
+
score
|
value
is a level name,
score
between levels,
confidence
when the model gives one
|
ORDER BY signals.x.score DESC
|
| At the default | The lever | |
|---|---|---|
| Money | $0.042 per million input tokens, about $6 per 100k judged events |
when
,
cacheKey
,
state
|
| Rate | 1,200 calls per minute per account |
budget.perMinute
,
cacheKey
|
| Latency |
up to 2 s before a
keep
event drains, client unaffected
|
timeoutMs
, a tight
when
|
| Egress | the whole event, before redaction |
state
,
providerOptions.gateway.zeroDataRetention
|
| Signal | Hook | Question | Why a rule cannot |
|---|---|---|---|
fault
|
enrich, cached | Who is responsible: user, us, upstream | A Stripe timeout and a null deref are both a 500 |
severity
|
enrich | noise, watch or page | Urgency depends on what failed and for whom |
retryable
|
enrich, cached | Would a retry succeed | Transient and permanent errors share status codes |
slow-cause
|
enrich | database, upstream, compute | Reading the timings takes a human |
silent-failure
|
keep | 200, but the customer left empty-handed | Sampling only keeps what a predicate can name |
webhook-ignored
|
keep | Acknowledged but not acted on | The provider only sees the 200 |
validation-bug
|
keep | The rejected input was valid | Looks identical to bad input |
turn-resolved
|
enrich | The agent did what was asked | A sampled LLM-as-judge misses the rest |
turn-looping
|
keep | Repeated a tool call without progress | Loops hide in successful turns |
turn-off-script
|
keep | Did something nobody asked for | Same |
audit-review
|
keep | This action deserves a reviewer | Risk is in the combination, not one field |
job-flaky
|
enrich | Succeeded only because it retried | The retry hides the cause |
| Option | Type | Notes |
|---|---|---|
name
|
string
|
Column name under
event.signals
. Letters, digits, dashes, starting with a letter. Unique across registered signals.
|
ask
|
string
|
The question, in plain English. |
when
|
(event) => boolean
|
Predicate over the event. No model call when
false
. Required with
keep
.
|
choice
|
Record
|
At least two options. Makes a choice signal. |
score
|
[level, level, ...]
|
Ordered lowest first, at least two. Makes a score signal. |
criteria
|
{ true?, false? }
|
Boolean signals only. What makes each answer true. |
keep
|
(verdict) => boolean
|
Promote the event past sampling when
true
. Never drops. Typed on the verdict the shape produces.
|
cacheKey
|
(event) => string | undefined
|
Reuse the verdict for events sharing a key.
undefined
skips caching. Last 1,000 verdicts per process.
|
| Option | Default | Notes |
|---|---|---|
signals
|
The signals to run. Duplicate names throw at startup. | |
model
|
'typesafe-ai/jev'
|
AI SDK evaluation model: a gateway id or a provider instance. |
budget.perMinute
|
600
|
Model calls per minute, shared by all signals, per process. |
budget.cooldownMs
|
30000
|
Pause after a failed call. |
timeoutMs
|
2000
|
Per call. An overrun aborts the call and counts as an error. |
state
|
whole event minus
signals
and
signalsModel
|
What the model reads. May return a string. |
maxStateChars
|
100000
|
Largest state sent, in characters of JSON. Larger events are skipped. |
providerOptions
|
Forwarded to the model call, e.g.
{ gateway: { zeroDataRetention: true } }
.
|
|
evaluate
|
AI SDK
experimental_evaluate
|
Replace the model call. Tests, record and replay. |
stampModel
|
false
|
Also write the answering model's id on
event.signalsModel
. Set it while comparing two models so their verdicts can be told apart in the drain; one more column on every judged event otherwise.
|
| Framework | Register |
|---|---|
| Nuxt, Nitro |
In a server plugin:
nitroApp.hooks.hook('evlog:emit:keep', plugin.keep)
and
nitroApp.hooks.hook('evlog:enrich', plugin.enrich)
|
| Next.js, standalone |
initLogger({ plugins: [plugin] })
|
| Hono, Express, Fastify, Elysia, oRPC, SvelteKit, React Router |
The middleware's
plugins
option:
evlog({ plugins: [plugin] })
|
createMiddlewareLogger
(toolkit)
|
plugins: [plugin]
on the call
|
| Shape |
value
|
confidence
|
Extra |
|---|---|---|---|
| Boolean |
boolean
|
probability of
value
|
|
| Choice | one of the option names | probability of that option, when the model returns a distribution | |
| Score | one of the level names (most likely) | probability of that level, when the model returns a distribution |
score: number
, probability-weighted position from
0
to
levels - 1
|