Identity Headers
Every drain request sent by evlog is tagged with two identity headers so receivers can identify the traffic:
| Header | Value |
|---|---|
User-Agent | evlog/<version> (Node / server runtimes only — browsers strip this header) |
X-Evlog-Source | The adapter name (axiom, datadog, otlp, posthog, sentry, better-stack, client, …) |
The browser-side evlog/http drain (used by the client transport) sets X-Evlog-Source: client instead, since browsers cannot override User-Agent.
Why
- Triage at the receiver. Quickly distinguish evlog traffic from other clients in the receiving system's logs.
- Track adapter usage and version drift. Roll out a new evlog version and watch the
User-Agentdistribution change centrally. - Debug a specific drain. Filter by
X-Evlog-Sourceto isolate one adapter's behavior in a sea of incoming requests.
Reading the version
Both constants are exported from evlog/toolkit so your drain (or your receiver) can reference the canonical values:
import { EVLOG_USER_AGENT, EVLOG_VERSION } from 'evlog/toolkit'
console.log(EVLOG_VERSION) // → "2.16.0"
console.log(EVLOG_USER_AGENT) // → "evlog/2.16.0"
Overriding from a custom drain
Adapters built with defineHttpDrain() automatically pass the drain name as source and the canonical evlog/<version> as userAgent. You don't need to think about it.
When you build a drain on top of httpPost from evlog/toolkit directly (e.g. for a fork with a different identity, or for a vendor that wants its own UA), pass source and/or userAgent to override:
import { httpPost } from 'evlog/toolkit'
await httpPost({
url: 'https://my-platform.example.com/ingest',
headers: { 'Content-Type': 'application/json' },
body: '[]',
timeout: 5000,
label: 'my-platform',
source: 'my-platform', // sent as X-Evlog-Source
userAgent: 'my-fork/1.0', // overrides the default User-Agent
// userAgent: false, // suppress the header entirely
})
Next steps
- Custom Drains —
defineHttpDraininjects identity headers automatically - Drain Pipeline — wrap any drain in batch + retry while keeping identity headers
Tail sampling
Decide post-hoc whether to keep an event with full knowledge of its outcome (status, duration, errors). The opposite of head sampling — keep all errors and slow requests while throwing away healthy noise.
Custom drains
Build a drain for any backend without a built-in adapter — defineHttpDrain for HTTP destinations, defineDrain for any other transport. Standardized config resolution, retries, timeouts, and identity headers handled for you.