Какой стек технологий используешь для логирования?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Мой подход к построению стека логирования в Go-проектах
За годы работы с распределенными системами на Go я выработал комплексный подход к логированию, который балансирует между детализацией отладки, производительностью и удобством анализа. Мой стек обычно состоит из нескольких слоев.
Базовый уровень: структурированное логирование
Я всегда использую структурированное логирование (structured logging) вместо примитивного вывода текста. В 95% проектов это библиотека Zap от Uber или Logrus:
// Пример с Zap
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("Обработка запроса",
zap.String("method", "GET"),
zap.String("path", "/api/v1/users"),
zap.Int("status", 200),
zap.Duration("duration", 150*time.Millisecond),
)
// Пример с Logrus
log.WithFields(log.Fields{
"user_id": 12345,
"action": "payment",
"amount": 99.99,
}).Info("Транзакция выполнена")
Почему именно они?
- Zap — невероятно быстрый (аллокации близки к нулю), идеален для high-load систем
- Logrus — более гибкий, с богатой экосистемой плагинов, отлично подходит для бизнес-приложений
- Обе поддерживают уровни логирования (Debug, Info, Warn, Error, Fatal), структурированный вывод в JSON и контекстные поля
Контекст и трассировка
Для микросервисных архитектур критически важна сквозная идентификация запросов (request tracing). Здесь я комбинирую:
- OpenTelemetry — стандарт де-факто для сбора телеметрии
- Context-based логирование — передача trace_id, span_id через контекст
// Установка trace_id в контекст и логгер
func HandleRequest(ctx context.Context, req *Request) {
traceID := otel.GetTraceID(ctx) // Например, из HTTP-заголовков
ctx = context.WithValue(ctx, "trace_id", traceID)
logger := log.WithContext(ctx).WithField("trace_id", traceID)
logger.Info("Начало обработки запроса")
// Все последующие логи будут автоматически содержать trace_id
}
Сбор и агрегация логов
Для продакшн-среды я настраиваю централизованный сбор логов:
- Loki от Grafana — мой основной выбор последние 2-3 года
- Отлично интегрируется с существующим стеком Grafana
- Эффективное хранение за счет индексирования только метаданных
- Мощный язык запросов LogQL
- Elastic Stack (ELK) — для сложных аналитических сценариев
- Fluentd/Fluent Bit — как агенты для сбора и трансформации логов
# Пример конфигурации вывода в Loki через Promtail
logging:
loki:
url: http://loki:3100/loki/api/v1/push
labels:
app: "users-service"
environment: "production"
pod: "${HOSTNAME}"
Обогащение и обработка логов
Перед отправкой в систему хранения я обязательно:
- Фильтрую чувствительные данные (пароли, токены, персональные данные)
- Обогащаю метаданными (версия приложения, хост, зона развертывания)
- Сэмплирую дебаг-логи в продакшне (только 1-5% записей)
// middleware для обогащения всех логов
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Обогащаем логгер контекстной информацией
logger := baseLogger.WithFields(log.Fields{
"http_method": r.Method,
"http_path": r.URL.Path,
"user_agent": r.UserAgent(),
"ip_address": r.RemoteAddr,
})
// Сохраняем в контекст
ctx := context.WithValue(r.Context(), "logger", logger)
next.ServeHTTP(w, r.WithContext(ctx))
logger.WithField("duration_ms", time.Since(start).Milliseconds()).
Info("Запрос завершен")
})
}
Мониторинг и алертинг
Логи не должны быть пассивным архивом. Я настраиваю:
- Grafana дашборды с ключевыми метриками из логов (error rate, latency percentiles)
- Prometheus алерты на основе метрик, извлеченных из логов
- Slack/Telegram уведомления для критических ошибок
Специфичные для Go особенности
Для Go-проектов я дополнительно:
- Использую pprof и expvar для логирования производительности
- Настраиваю panic recovery с детальным логированием стека вызовов
- Применяю interface-based логирование для тестирования
// Интерфейс для легкого тестирования
type Logger interface {
Debug(msg string, fields ...zap.Field)
Info(msg string, fields ...zap.Field)
Error(msg string, fields ...zap.Field)
With(fields ...zap.Field) Logger
}
// Внедрение зависимости
type Service struct {
logger Logger
}
func NewService(logger Logger) *Service {
return &Service{logger: logger}
}
Эволюция подхода
Мой стек постоянно эволюционирует:
- Раньше часто использовал Sentry для error tracking (и до сих пор для некоторых проектов)
- Zero-alloc логгеры для ultra-high performance сценариев (сотни тысяч RPS)
- Адаптивное логирование с автоматическим увеличением детализации при появлении ошибок
Ключевой принцип: логи должны быть не просто записанными событиями, а полноценным источником observability, интегрированным с метриками и трассировкой в единую картину работы системы.
В среднем проекте этот стек позволяет сократить время поиска проблем с часов до минут, при этом добавляя менее 1% overhead к времени ответа приложения.