← Назад к вопросам

Какой стек технологий используешь для логирования?

2.0 Middle🔥 132 комментариев
#Основы Go

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Мой подход к построению стека логирования в 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). Здесь я комбинирую:

  1. OpenTelemetry — стандарт де-факто для сбора телеметрии
  2. 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. Фильтрую чувствительные данные (пароли, токены, персональные данные)
  2. Обогащаю метаданными (версия приложения, хост, зона развертывания)
  3. Сэмплирую дебаг-логи в продакшне (только 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 к времени ответа приложения.