package lambdalog
import (
"context"
"io"
"testing"
"time"
)
// BenchmarkLogger_InfoNoAttrs measures the cost of a single Info call with
// no attributes. The goal of this benchmark is to keep the allocation count
// at or below one per call -- one for the encoded line.
func BenchmarkLogger_InfoNoAttrs(b *testing.B) {
l := New(io.Discard)
l.now = func() time.Time { return time.Unix(0, 0) }
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Info("ping")
}
}
// BenchmarkLogger_InfoWithAttrs logs a record that carries four attributes.
// This covers the common Lambda shape: rid + user id + op name + latency.
func BenchmarkLogger_InfoWithAttrs(b *testing.B) {
l := New(io.Discard).With("svc", "checkout")
l.now = func() time.Time { return time.Unix(0, 0) }
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Info("order placed",
"rid", "req-1",
"user", 42,
"op", "create",
"latency_ms", 17,
)
}
}
// BenchmarkLogger_SampledHotPath exercises the fast-reject branch of the
// sampler: nearly every call should be discarded. The allocation count
// should be zero because we never reach the encoder.
func BenchmarkLogger_SampledHotPath(b *testing.B) {
l := New(io.Discard).Sampled("hot", 1000)
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
l.Info("hot path")
}
}
// BenchmarkLogger_100K emits 100k records per iteration. The test harness
// uses it to produce a stable baseline for regression detection.
func BenchmarkLogger_100K(b *testing.B) {
l := New(io.Discard)
l.now = func() time.Time { return time.Unix(0, 0) }
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
for j := 0; j < 100_000; j++ {
l.Info("hello", "seq", j)
}
}
}
// BenchmarkEncodeRecord isolates the encoder from the writer/mutex path so
// the numbers reflect encoding cost alone.
func BenchmarkEncodeRecord(b *testing.B) {
attrs := []attr{
{Key: "svc", Value: "checkout"},
{Key: "rid", Value: "req-1"},
}
extra := []any{"user", 42, "op", "create"}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = encodeRecord(LevelInfo, "order placed", attrs, extra)
}
}
// BenchmarkFromContext covers the request-ID extraction path. The benchmark
// uses a pre-built context that already has an override set.
func BenchmarkFromContext(b *testing.B) {
l := New(io.Discard)
ctx := WithRequestID(context.Background(), "req-xyz")
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = l.FromContext(ctx)
}
}
// BenchmarkParseLevel measures the cost of parsing a level string -- low
// enough that it can live on the hot path of a per-request logger builder.
func BenchmarkParseLevel(b *testing.B) {
inputs := []string{"info", "DEBUG", "warn", "3", "i"}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = ParseLevel(inputs[i%len(inputs)])
}
}