// Package sampler defines a pluggable interface for deciding whether a
// given log record should be emitted. Implementations live in sibling
// packages: random, adaptive, threshold.
//
// See mercemay.top/src/lambdalog/.
package sampler
import "time"
// Decision is the result of a call to Sampler.Sample.
type Decision int
const (
// Drop discards the record.
Drop Decision = iota
// Emit writes the record normally.
Emit
// Tag writes the record but adds a {"sampled": true} field so
// aggregators can distinguish head-sampled records.
Tag
)
// Input is the subset of record metadata samplers need. Keeping it separate
// from encoder.Record allows the sampler tree to compile without a
// dependency on the encoder package.
type Input struct {
Level string
Message string
Now time.Time
QPSHint float64
Attempt int
}
// Sampler is the contract every implementation satisfies.
type Sampler interface {
Sample(Input) Decision
Name() string
}
// Chain composes multiple samplers with short-circuit semantics: the first
// one that returns Drop wins. Tag propagates through but does not
// short-circuit; Emit yields to the next sampler.
type Chain []Sampler
// Name returns a deterministic name derived from the chain members.
func (c Chain) Name() string {
if len(c) == 0 {
return "chain(empty)"
}
name := "chain("
for i, s := range c {
if i > 0 {
name += ","
}
name += s.Name()
}
return name + ")"
}
// Sample applies each child in order. See Chain for semantics.
func (c Chain) Sample(in Input) Decision {
result := Emit
for _, s := range c {
d := s.Sample(in)
switch d {
case Drop:
return Drop
case Tag:
result = Tag
}
}
return result
}
// Always is a trivial sampler that returns a fixed Decision. It is useful
// as a fallback or in tests.
type Always Decision
// Sample returns the underlying Decision.
func (a Always) Sample(Input) Decision { return Decision(a) }
// Name reports "always-emit" / "always-drop" / "always-tag".
func (a Always) Name() string {
switch Decision(a) {
case Drop:
return "always-drop"
case Tag:
return "always-tag"
default:
return "always-emit"
}
}