> Когда логирование хорошо использовать, а когда лучше не использовать (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Employcity

Стек: Go

> Пример ответа

Короткий ответ

Логирование хорошо использовать для диагностики, аудита и мониторинга поведения системы в production, а также для отладки распределённых взаимодействий. Не стоит использовать логи как основное средство отладки на этапе разработки (лучше debugger), для передачи данных между сервисами, хранения чувствительной информации или как замену метрик и трейсинга. Ключевой принцип - логировать события, а не каждое действие, и помнить о стоимости I/O и объёме хранилища.

Подробное объяснение

Логирование оправдано в следующих сценариях:

  • Диагностика инцидентов - когда нужно восстановить последовательность событий после сбоя или ошибки.
  • Аудит и compliance - фиксация действий пользователей, изменений данных, доступа к ресурсам.
  • Мониторинг бизнес-процессов - логи как источник данных для аналитики (например, частота запросов, ошибки валидации).
  • Отладка распределённых систем - когда нет единой точки выполнения и debugger не применим, логи помогают проследить путь запроса через сервисы.
  • Постепенное развёртывание - при canary-деплое или feature flags, когда нужно быстро понять поведение новой версии.

Не стоит использовать логирование:

  • Для отладки локального кода - debugger, unit-тесты и интеграционные тесты дают больше информации и быстрее.
  • Как транспорт данных - если сервисы обмениваются данными через логи, это создаёт хрупкую связку и проблемы с парсингом.
  • Для чувствительных данных - пароли, токены, персональные данные (PII) в логах - это риск утечки и нарушение GDPR/152-ФЗ.
  • Вместо метрик - если нужно считать количество запросов или latency, используй метрики (Prometheus), а не парсинг логов.
  • В горячем пути (hot path) - логирование в цикле с высокой частотой вызовов убивает производительность из-за блокировок и аллокаций.

Важно различать уровни логирования: debug, info, warn, error. В production обычно включают info и выше, debug - только при целенаправленной диагностике. Также стоит помнить про структурированные логи (JSON) - они лучше поддаются агрегации и фильтрации, чем plain text.

На практике

Для Go-приложений используй стандартную библиотеку log/slog (Go 1.21+) или zerolog/zap для структурированного логирования. Настрой уровни через конфигурацию окружения, а не хардкод. Обязательно добавь correlation ID в логи для трассировки запросов между сервисами.

Практические рекомендации:

  • Логируй вход и выход из критических операций (например, обработка платежа), но не логируй каждый шаг внутри.
  • Для ошибок всегда включай stack trace, но только на уровне error, не на info.
  • Используй middleware для HTTP-запросов: метод, path, status code, duration - это покрывает 80% потребностей.
  • Настрой ротацию логов и retention policy, иначе диск заполнится.
  • В Kubernetes логи пиши в stdout/stderr, а сборку оставь на sidecar или агента.

Пример плохой практики - логирование каждого SQL-запроса в production с полными параметрами. Хорошая практика - логировать только медленные запросы (>100ms) и ошибки.

Пример кода

GO
package main
import (
"log/slog"
"net/http"
"time"
)
// Middleware для логирования HTTP-запросов
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Обёртка для захвата status code
sw := &statusWriter{ResponseWriter: w}
next.ServeHTTP(sw, r)
slog.Info("http_request",
"method", r.Method,
"path", r.URL.Path,
"status", sw.status,
"duration_ms", time.Since(start).Milliseconds(),
)
})
}
type statusWriter struct {
http.ResponseWriter
status int
}
func (w *statusWriter) WriteHeader(code int) {
w.status = code
w.ResponseWriter.WriteHeader(code)
}
// Пример использования в main
func main() {
// Настройка уровня логирования из env
level := slog.LevelInfo
if debug := os.Getenv("DEBUG"); debug == "1" {
level = slog.LevelDebug
}
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: level,
})))
mux := http.NewServeMux()
mux.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
// Не логируем каждый запрос здесь - это делает middleware
w.Write([]byte("ok"))
})
http.ListenAndServe(":8080", loggingMiddleware(mux))
}

Как отвечать на собеседовании

Начни с чёткого разделения: логи - это инструмент для эксплуатации, а не для разработки. Приведи конкретные примеры из своего опыта: когда логи спасли ситуацию при инциденте, и когда логи мешали (например, слишком много шума). Покажи понимание trade-off между полнотой логирования и производительностью.

Упомяни, что в современных системах логи - это часть observability (logs, metrics, traces), и важно не смешивать их роли. Расскажи про структурированные логи и correlation ID - это покажет, что ты работал с распределёнными системами.

Если спросят про конкретные библиотеки - сравни slog и zerolog: slog стандартный и достаточен для большинства задач, zerolog быстрее и имеет больше фич, но добавляет зависимость.

Что проверяет интервьюер

Интервьюер оценивает:

  • Понимание назначения логирования в production-системах, а не только в dev-среде.
  • Умение различать логи, метрики и трейсинг.
  • Знание практических аспектов: уровни, структурирование, ротация, чувствительные данные.
  • Опыт работы с Go-экосистемой логирования.
  • Способность принимать решения: что логировать, а что нет, исходя из стоимости и пользы.

Для senior-позиции важно показать, что ты думаешь о логировании как о части архитектуры системы, а не просто вызываешь fmt.Println.

Типичные ошибки

  • Логирование всего подряд - "на всякий случай" приводит к шуму, который невозможно анализировать.
  • Логирование чувствительных данных - пароли, токены, номера карт в логах - это прямой путь к утечке.
  • Синхронное логирование в hot path - блокирующий I/O в цикле обработки запросов убивает производительность.
  • Использование fmt.Println вместо структурированного логирования - теряется контекст и усложняется агрегация.
  • Отсутствие уровней - когда всё пишется на одном уровне, невозможно фильтровать.
  • Логирование как замена метрик - попытка считать количество ошибок через grep по логам вместо использования Prometheus.
  • Игнорирование ошибок логирования - если логгер сам падает, приложение не должно падать вместе с ним.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью