Оптимизация производительности
DD разработан для высокой производительности, ниже приведены рекомендации по дальнейшей оптимизации.
Низкоаллокационная оптимизация
DD минимизирует выделение памяти на горячих путях:
IsLevelEnabled()использует атомарные операции, без блокировок- Структурированные поля используют предварительно выделенные буферы
- Избегает форматирования сообщений при несовпадении уровня логирования
Проверка уровня
На высокочастотных путях сначала проверяйте уровень, чтобы избежать ненужного конструирования полей:
go
// Рекомендуется: сначала проверить уровень
if logger.IsDebugEnabled() {
logger.DebugWith("Подробная информация",
dd.String("data", expensiveToString()),
dd.Int("size", len(largeSlice)),
)
}
// Не рекомендуется: всегда конструировать поля
logger.DebugWith("Подробная информация",
dd.String("data", expensiveToString()),
)Буферизованная запись
Используйте BufferedWriter для уменьшения системных вызовов I/O:
go
fw, _ := dd.NewFileWriter("logs/app.log", dd.DefaultFileWriterConfig())
bwCfg := dd.DefaultBufferedWriterConfig()
bwCfg.BufferSize = 8192
bw, _ := dd.NewBufferedWriter(fw, bwCfg) // 8КБ буфер
logger, _ := dd.New(dd.Config{
Targets: []dd.OutputTarget{dd.CustomOutput(bw)},
})
defer logger.Close() // Close автоматически вызывает FlushРазмер буфера
Рекомендуется 4КБ-16КБ. Слишком маленький буфер неэффективно уменьшает системные вызовы, слишком большой увеличивает использование памяти и задержку.
Сэмплирование логов
В сценариях с высокой пропускной способностью можно включить сэмплирование логов для уменьшения дублирующихся логов:
go
logger.SetSampling(&dd.SamplingConfig{
Enabled: true,
Initial: 100, // Первые 100 записываются полностью
Thereafter: 10, // Затем записывается 1 из 10
Tick: time.Minute, // Сброс счётчика каждую минуту
})
// Динамическая регулировка во время выполнения
cfg := logger.GetSampling()Оптимизация записи в файл
Разумная конфигурация ротации
go
fw, _ := dd.NewFileWriter("logs/app.log", dd.DefaultFileWriterConfig())
// По умолчанию: 100МБ / 30 дней / 10 резервных копий- Слишком маленькие файлы приводят к частой ротации, увеличивая I/O
- Слишком много резервных копий занимает дисковое пространство
- Настраивайте параметры в соответствии с фактическим объёмом логов
Разделение по файлам
go
// Разделение по уровням
infoWriter, _ := dd.NewFileWriter("logs/info.log", dd.DefaultFileWriterConfig())
errorWriter, _ := dd.NewFileWriter("logs/error.log", dd.DefaultFileWriterConfig())Управление Writer
Динамическое добавление/удаление Writer
go
// Динамическое добавление во время выполнения
logger.AddWriter(newWriter)
// Удаление ненужных Writer
logger.RemoveWriter(oldWriter)Избегание избыточного количества Writer
Каждый Writer увеличивает задержку записи. Рекомендуется не более 3-4 Writer.
Оптимизация полей
Использование типизированных полей
go
// Рекомендуется: типизированные конструкторы
dd.Int("count", 42)
dd.String("name", "test")
// Избегать: Any (требует дополнительного утверждения типа)
dd.Any("count", 42)Избегание больших объектов
go
// Не рекомендуется: запись больших объектов
logger.InfoWith("Данные", dd.Any("payload", hugeStruct))
// Рекомендуется: записывать только ключевую информацию
logger.InfoWith("Данные",
dd.Int("count", len(items)),
dd.String("first", items[0].Name),
)Закрытие и очистка
go
// Ожидание завершения фильтрующих горутин
logger.WaitForFilterGoroutines(3 * time.Second)
// Изящное завершение, ожидание сброса всех буферов
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
logger.Shutdown(ctx)Следующие шаги
- Цели вывода -- API FileWriter, BufferedWriter
- Конфигурация -- параметры конфигурации производительности
- Контрольный список для продакшена -- проверка перед развёртыванием