Skip to content

Сэмплирование логов

В сценариях с высокой пропускной способностью (логирование HTTP-запросов, обработка потоков событий) запись каждого события создаёт огромный объём данных. Функция сэмплирования DD сохраняет логи пропорционально, контролируя объём логов, но по-прежнему отражая общие тенденции.

Принцип сэмплирования

DD использует стратегию сэмплирования на основе счётчика:

┌──────────────────────────────────────────────────────────────────┐
│  Запросы 1-100   →  все логируются (фаза Initial)                │
│  Запрос 101      →  пропущен                                     │
│  Запрос 102      →  пропущен                                     │
│  ...                                                             │
│  Запрос 110      →  логируется (1 из каждых Thereafter=10)       │
│  Запрос 111      →  пропущен                                     │
│  ...                                                             │
│  (Tick истекает → счётчик сбрасывается, вход в фазу Initial)     │
└──────────────────────────────────────────────────────────────────┘
ПараметрОписаниеТипичное значение
EnabledВключить сэмплированиеtrue
InitialПервые N записей всегда логируются100
ThereafterЛогировать 1 из каждых N после Initial10
TickИнтервал сброса счётчика (0 = без сброса)1s / 1m

Быстрый старт

Включение при конфигурации

go
package main

import (
    "log"
    "time"

    "github.com/cybergodev/dd"
)

func main() {
    cfg := dd.DefaultConfig()
    cfg.Sampling = &dd.SamplingConfig{
        Enabled:    true,
        Initial:    100,             // Первые 100 всегда логируются
        Thereafter: 10,              // Затем логируется 1 из каждых 10
        Tick:       time.Second,     // Сброс счётчика каждую секунду
    }

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

    // Имитация логирования с высокой нагрузкой
    for i := 0; i < 1000; i++ {
        logger.InfoWith("request processed",
            dd.Int("seq", i),
        )
    }
    // Фактический вывод: первые 100 + 90 из оставшихся 900 = 190 записей
}

Переключение во время выполнения

go
// Включить сэмплирование
logger.SetSampling(&dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 20,
    Tick:       0, // Без авто-сброса
})

// Отключить сэмплирование (возобновить полное логирование)
logger.SetSampling(nil)

// Запросить текущую конфигурацию сэмплирования
sc := logger.GetSampling()
if sc != nil {
    fmt.Printf("Sampling: Initial=%d, Thereafter=%d\n", sc.Initial, sc.Thereafter)
}

Подсказка Сэмплирование глобального Logger

Функции уровня пакета dd.SetSampling() и dd.GetSampling() работают напрямую с глобальным Logger.

Подробное описание параметров

Initial: окно полного объёма в начале

Initial гарантирует, что первые N записей после запуска или сброса Tick логируются все, обеспечивая:

  • Сохранение логов инициализации на этапе запуска
  • Полные записи при кратковременных всплесках трафика
  • Видимость состояния на начало периода после сброса Tick

Thereafter: частота сэмплирования

ThereafterЭффектДоля сохранения (после Initial)
1Логировать каждую запись (= отключено)100%
10Логировать 1 из каждых 1010%
100Логировать 1 из каждых 1001%
0Прекратить логирование после Initial0%

Предупреждение Thereafter=0

Thereafter=0 означает полную остановку логирования после фазы Initial. Это полезно в некоторых сценариях (например, нужны только логи запуска), но убедитесь, что не пропустите важную информацию.

Tick: периодический сброс

go
// Вариант A: сброс каждую секунду (обнаружение всплесков)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 100, Thereafter: 10,
    Tick: time.Second,
}

// Вариант B: без сброса (глобальный подсчёт, долгосрочное сокращение)
Sampling: &dd.SamplingConfig{
    Enabled: true, Initial: 1000, Thereafter: 100,
    Tick: 0,
}

После сброса Tick счётчик обнуляется и снова входит в фазу полного объёма Initial. Полезно для наблюдения за шаблонами трафика в каждом периоде.

Типичные сценарии

Сценарий 1: логирование HTTP-запросов

go
// Высоконагруженный API: первые 100 полностью, затем 10% сэмплирование, сброс каждую секунду
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    100,
    Thereafter: 10,
    Tick:       time.Second,
}

Сценарий 2: логирование фоновых задач

go
// Пакетная обработка: первые 50 полностью, затем 1 из каждых 100, без сброса
cfg.Sampling = &dd.SamplingConfig{
    Enabled:    true,
    Initial:    50,
    Thereafter: 100,
    Tick:       0,
}

Сценарий 3: переключение режима отладки

go
// Нормальный режим: сэмплирование
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

// Диагностика: отключить сэмплирование, полное логирование
logger.SetSampling(nil)

// Исправлено: восстановить сэмплирование
logger.SetSampling(&dd.SamplingConfig{
    Enabled: true, Initial: 10, Thereafter: 50,
})

Потокобезопасность

Сэмплирование использует атомарные операции (atomic.Int64) для счётчика и мьютекс для сброса Tick. Дополнительная синхронизация не требуется при конкурентной записи логов из нескольких goroutine.

Подсказка Логи Fatal обходят сэмплирование

Даже при включённом сэмплировании логи уровня Fatal всегда записываются — Fatal должен быть зафиксирован перед выходом из программы и никогда не должен пропускаться сэмплированием.

Следующие шаги