Skip to content

Log Sampling ​

In high-throughput scenarios (HTTP request logging, event stream processing), logging every entry produces massive data. DD's sampling feature retains logs proportionally, controlling log volume while still reflecting overall trends.

Sampling Principle ​

DD uses a counter-based sampling strategy:

┌──────────────────────────────────────────────────────────┐
│  Requests 1-100  →  all logged (Initial phase)           │
│  Request 101     →  skipped                               │
│  Request 102     →  skipped                               │
│  ...                                                      │
│  Request 110     →  logged (1 of every Thereafter=10)     │
│  Request 111     →  skipped                               │
│  ...                                                      │
│  (Tick expires → counter resets, re-enters Initial phase) │
└──────────────────────────────────────────────────────────┘
ParameterDescriptionTypical Value
EnabledEnable samplingtrue
InitialFirst N entries always logged100
ThereafterLog 1 out of every N after Initial10
TickCounter reset interval (0 = never reset)1s / 1m

Quick Start ​

Enable at Configuration ​

go
package main

import (
    "log"
    "time"

    "github.com/cybergodev/dd"
)

func main() {
    cfg := dd.DefaultConfig()
    cfg.Sampling = &dd.SamplingConfig{
        Enabled:    true,
        Initial:    100,             // First 100 always logged
        Thereafter: 10,              // Then log 1 of every 10
        Tick:       time.Second,     // Reset counter every second
    }

    logger, err := dd.New(cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer logger.Close()

    // Simulate high-throughput logging
    for i := 0; i < 1000; i++ {
        logger.InfoWith("request processed",
            dd.Int("seq", i),
        )
    }
    // Actual output: first 100 + 90 of remaining 900 = 190 entries
}

Runtime Toggle ​

go
// Enable sampling
logger.SetSampling(&dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 20,
    Tick:       0, // No auto-reset
})

// Disable sampling (resume full logging)
logger.SetSampling(nil)

// Query current sampling config
sc := logger.GetSampling()
if sc != nil {
    fmt.Printf("Sampling: Initial=%d, Thereafter=%d\n", sc.Initial, sc.Thereafter)
}

Global Logger sampling

Package-level functions dd.SetSampling() and dd.GetSampling() operate on the global Logger directly.

Parameter Details ​

Initial: Initial Full-Volume Window ​

Initial guarantees that the first N entries after startup or Tick reset are all logged, ensuring:

  • Startup-phase initialization logs are not lost
  • Short burst traffic has complete records
  • Period-start state is visible after Tick reset

Thereafter: Sampling Rate ​

ThereafterEffectRetention Rate (after Initial)
1Log every entry (= disabled)100%
10Log 1 of every 1010%
100Log 1 of every 1001%
0Stop logging after Initial0%

Thereafter=0

Thereafter=0 means completely stop logging after the Initial phase. Useful in some scenarios (e.g., only startup logs needed), but ensure no important information is missed.

Tick: Periodic Reset ​

go
// Option A: Reset every second (burst detection)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 100, Thereafter: 10,
    Tick: time.Second,
}

// Option B: No reset (global count, long-term reduction)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 1000, Thereafter: 100,
    Tick: 0,
}

After Tick reset, the counter resets to zero and re-enters the Initial full-volume phase. Useful for observing traffic patterns per period.

Typical Scenarios ​

Scenario 1: HTTP Request Logging ​

go
// High-traffic API: first 100 full, then 10% sampled, reset every second
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    100,
    Thereafter: 10,
    Tick:       time.Second,
}

Scenario 2: Background Task Logging ​

go
// Batch processing: first 50 full, then 1 of every 100, no reset
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 100,
    Tick:       0,
}

Scenario 3: Debug Mode Toggle ​

go
// Normal: sampled
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

// Troubleshooting: disable sampling, full logging
logger.SetSampling(nil)

// Fixed: restore sampling
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

Thread Safety ​

Sampling uses atomic operations (atomic.Int64) for the counter and a mutex for Tick reset. No additional synchronization needed for concurrent logging from multiple goroutines.

Fatal logs bypass sampling

Even with sampling enabled, Fatal-level logs are always written — Fatal must be recorded before program exit and should never be skipped by sampling.

Next Steps ​