> Когда логирование хорошо использовать, а когда лучше не использовать (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) и ошибки.
Пример кода
GOpackage mainimport ("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 codesw := &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.ResponseWriterstatus int}func (w *statusWriter) WriteHeader(code int) {w.status = codew.ResponseWriter.WriteHeader(code)}// Пример использования в mainfunc main() {// Настройка уровня логирования из envlevel := slog.LevelInfoif 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) {// Не логируем каждый запрос здесь - это делает middlewarew.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.
- Игнорирование ошибок логирования - если логгер сам падает, приложение не должно падать вместе с ним.
> Похожие задачи по Go
Как избежать race condition при одновременном чтении и записи в таблицу транзакций в Go
Для чего нужно логирование
Почему нельзя заменить WITH (CTE) на подзапрос в SQL
Что такое паттерн разделения на чтение и запись
> Похожие задачи по backend
Как избежать race condition при одновременном чтении и записи в таблицу транзакций в Go
Для чего нужно логирование
Почему нельзя заменить WITH (CTE) на подзапрос в SQL
Что такое паттерн разделения на чтение и запись
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью