В .NET 9 появилась интересная функциональность — Log Buffering, которая позволяет буферизовать логи в памяти и выводить их только при определенных условиях. Меня заинтересовала эта идея, что я решил реализовать аналогичный механизм для Go. Так появился EmitLog — пакет для условной буферизации логов.
Проблема традиционного логирования
Представьте типичный веб-сервис с детальным логированием:
func ProcessPayment(ctx context.Context, paymentID string) error {
log.Debug("Starting payment processing")
log.Debug("Validating payment data")
log.Debug("Checking user balance")
log.Debug("Connecting to payment gateway")
log.Info("Payment processed successfully")
return nil
}
При высокой нагрузке такой сервис генерирует огромное количество логов:
99% успешных транзакций = миллионы ненужных debug-логов
Высокие расходы на хранение в системах типа ELK, Datadog
Замедление поиска важной информации в море рутинных записей
Но если убрать debug-логи совсем, то при ошибке мы потеряем контекст происходящего.
Решение: условная буферизация
EmitLog решает эту дилемму:
Буферизует все логи запроса в памяти
Анализирует результат выполнения
Решает — сохранить логи или отбросить
// При ошибке — видим полный контекст
[ERROR] Payment failed: insufficient funds
[DEBUG] Starting payment processing
[DEBUG] Validating payment data
[DEBUG] Checking user balance
[INFO] User balance: $50, required: $100
// При успехе — логи отбрасываются (или сохраняются с вероятностью 5%)
Цепочка контекста логирования
Вот как выглядит полный flow:
HTTP Request → Middleware создает BufferingWriter
↓
Создает logger с контекстом запроса
↓
Logger помещается в context.Context
↓
Handler извлекает logger: GetLoggerFromContext(ctx)
↓
Все логи пишутся в буфер в памяти
↓
[Возникла ошибка?]
↙ ↘
Да Нет
↓ ↓
Flush всех [Random < SaveRate?]
логов ↙ ↘
Да Нет
↓ ↓
Сохранить Отбросить
Практическое использование
Базовая настройка
func main() {
// Настраиваем zerolog
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
// Конфигурация EmitLog
config := emitlog.Config{
SaveRate: 10.0, // 10% успешных запросов
BufferingEnabled: true,
FlushOnError: true, // Всегда при ошибках
FlushOnWarn: false, // Игнорируем warnings
BufferSize: 64 * 1024, // 64KB на запрос
}
// Применяем middleware
handler := emitlog.Middleware(config, os.Stderr)(mux)
http.ListenAndServe(":8080", handler)
}
Использование в хендлерах
func ProcessOrderHandler(w http.ResponseWriter, r *http.Request) {
// Извлекаем настроенный logger из контекста
logger := emitlog.GetLoggerFromContext(r.Context())
logger.Debug().Msg("Parsing order request")
var order Order
if err := json.NewDecoder(r.Body).Decode(&order); err != nil {
logger.Error().Err(err).Msg("Failed to parse order")
http.Error(w, "Bad request", 400)
return // Все debug-логи будут выведены!
}
logger.Debug().
Str("order_id", order.ID).
Float64("amount", order.Amount).
Msg("Processing order")
if err := processPayment(order); err != nil {
logger.Error().Err(err).Msg("Payment failed")
http.Error(w, "Payment failed", 500)
return // Видим полный контекст ошибки
}
logger.Info().Msg("Order processed successfully")
w.WriteHeader(200)
// При успехе логи сохранятся только с вероятностью SaveRate
}
Преимущества подхода
1. Снижение объема логов
Традиционное логирование:
1M запросов/день × 10 логов/запрос = 10M записей
При SaveRate = 5%: 50K записей + логи всех ошибок
Экономия: 95%+ на хранении
2. Полный контекст при ошибках
Когда что-то идет не так, вы видите всю историю запроса:
Все debug-сообщения
Промежуточные состояния
Точную последовательность операций
3. Производительность
Снижение I/O: меньше записей на диск/сеть
Батчинг: при сбросе логи пишутся одним блоком
4. Гибкая конфигурация
// Development
config.BufferingEnabled = false // Видим все сразу
config.SaveRate = 100.0 // Сохраняем все
// Staging
config.SaveRate = 50.0 // Половину для анализа
config.FlushOnWarn = true // Warnings тоже важны
// Production
config.SaveRate = 5.0 // Только 5% успешных
config.BufferSize = 128 * 1024 // Больше буфер
Когда использовать EmitLog
✅ Идеально подходит для:
Высоконагруженных API с детальным логированием
Микросервисов с дорогой инфраструктурой логирования
Систем, где ошибки требуют полного контекста для отладки
❌ Не подходит для:
Критичных аудит-логов (compliance)
Реального времени мониторинга
Маленьких приложений с небольшим объемом логов
Заключение
Код проекта доступен на GitHub
Буду рад фидбеку и предложениям по развитию проекта!
мой телерамм для связи https://t.me/atsaregorodtsev