Head Sampling
Head sampling decides as each record is emitted: keepn% of a level, drop the rest.
ERROR and WARNING above are untouched. Set a level explicitly if you really do want to thin it — { ERROR: 50 } is honored.
Sampling is off unless at least one level is below 100. A tail block on its own does nothing, because only head-dropped records are ever buffered.
Tail Sampling
A 10%INFO rate is fine until a request fails and nine out of ten of its logs are gone. Tail sampling fixes that: head-dropped records are held for the duration of the request, then replayed if the finished request matches any tail rule.
Path globs
Everything else is literal, and patterns are anchored, so
/v1/users matches only /v1/users.
Buffer cap
Records are buffered per request, capped so a pathological handler cannot grow memory without bound. Anything past the cap is dropped and cannot be rescued.What It Costs
A head-dropped record skips context merging, redaction, formatting, and every sink — only its rawdata and the duration at capture are retained. A request that is never rescued therefore pays close to nothing; a rescued one pays the full pipeline at replay time, with each record keeping the duration it had when it was captured rather than the duration at replay.
Scope
Tail buffering applies to HTTP requests the plugin opens and closes. WebSocket lifecycle logs are head-sampled but never buffered — a long-lived socket has no request end to rescue against — and the same is true for acreateLogger instance used outside the plugin.
Recipes
Cost control — keep the shape of traffic, keep all the failures:Sampling and Adapters
Sampling runs before every sink, so a dropped record never reaches transports, file logging, or the console. That is the point: it is the cheapest place to cut what Datadog, Better Stack, or PostHog will charge you for.Validation
Bad sampling config throws at plugin construction rather than failing quietly at runtime:head.<LEVEL>must be a number between0and100tail.statusandtail.durationMsmust be non-negative numberstail.pathsmust contain non-empty stringsmaxBufferedPerRequestmust be a non-negative integer