Логирование в Go приложениях: лучшие практики на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему логирование в Go — это не просто fmt.Println

Логирование — это глаза и уши любого приложения, работающего в production. В мире Go (Golang) подход к логированию претерпел значительную эволюцию: от простого вывода в консоль через fmt.Println до мощных структурных логгеров, способных выдавать JSON-совместимые записи для систем централизованного сбора логов (ELK, Grafana Loki, Datadog).

В этой статье мы разберем лучшие практики логирования в Go на практике, а не в теории. Вы узнаете, как настроить уровни логирования, использовать структурированные логи, избегать "шума" и правильно обрабатывать ошибки. Все примеры кода готовы к использованию в ваших проектах.

1. Стандартная библиотека log: когда её достаточно, а когда нет

Go предоставляет встроенный пакет log из стандартной библиотеки. Он прост, но имеет существенные ограничения.

Базовый пример

package main

import (
    "log"
    "os"
)

func main() {
    // Настройка вывода в файл
    file, _ := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    defer file.Close()
    log.SetOutput(file)

    log.Println("Приложение запущено")
    log.Printf("Пользователь %s авторизован", "admin")
    log.Fatal("Критическая ошибка: не удалось подключиться к БД")
}

Плюсы:

  • Не требует установки сторонних зависимостей.
  • Простота использования для небольших скриптов или CLI-утилит.

Минусы:

  • Нет уровней логирования (только Print, Fatal, Panic).
  • Формат вывода фиксированный (дата, время, сообщение).
  • Нет структурированных полей (ключ-значение).
  • Плохо парсится системами сбора логов.

Вывод: для production-сервисов, микросервисной архитектуры или любого приложения, где требуется мониторинг, стандартный log не подходит. Переходите к структурному логированию.

2. Структурное логирование с zerolog: современный стандарт

Библиотека zerolog — один из самых популярных и быстрых логгеров в Go. Она поддерживает уровни логирования, JSON-вывод по умолчанию и минимальное выделение памяти.

Установка

go get -u github.com/rs/zerolog/log

Пример настройки и использования

package main

import (
    "os"
    "time"

    "github.com/rs/zerolog"
    "github.com/rs/zerolog/log"
)

func main() {
    // Настройка формата времени
    zerolog.TimeFieldFormat = time.RFC3339

    // Вывод в консоль с цветами (для разработки)
    log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339})

    // Для production — JSON в stdout
    // log.Logger = zerolog.New(os.Stdout).With().Timestamp().Logger()

    log.Info().Msg("Сервер запущен на порту 8080")
    log.Debug().Str("module", "auth").Msg("Попытка входа пользователя")
    log.Error().Err(err).Str("user_id", "12345").Msg("Ошибка авторизации")
    log.Fatal().Int("attempt", 3).Msg("Превышено количество попыток")
}

Уровни логирования в zerolog

  • Trace (log.Trace()) — для глубокой отладки.
  • Debug (log.Debug()) — отладочная информация.
  • Info (log.Info()) — обычные события.
  • Warn (log.Warn()) — предупреждения (не ошибки).
  • Error (log.Error()) — ошибки, требующие внимания.
  • Fatal (log.Fatal()) — фатальные ошибки (завершает программу).
  • Panic (log.Panic()) — паника с выводом стека.

Лучшая практика: в production установите минимальный уровень Info или Warn, чтобы не захламлять логи debug-сообщениями.

zerolog.SetGlobalLevel(zerolog.InfoLevel)

3. Лучшие практики логирования: структура, контекст и ошибки

Чтобы логи были полезными, а не "шумом", следуйте этим правилам.

3.1. Всегда добавляйте контекст

Лог без контекста — бесполезен. Вместо log.Info().Msg("Ошибка") пишите:

log.Error().
    Str("request_id", reqID).
    Str("method", "POST").
    Int("status", 500).
    Err(err).
    Msg("Не удалось обработать запрос")

3.2. Не логируйте чувствительные данные

Никогда не выводите пароли, токены, номера кредитных карт или персональные данные (GDPR, 152-ФЗ).

// ПЛОХО
log.Info().Str("password", userPassword).Msg("Логин")

// ХОРОШО
log.Info().Str("user_id", user.ID).Bool("authenticated", true).Msg("Успешный вход")

3.3. Используйте middleware для HTTP-сервера

Автоматически логируйте входящие запросы, статус ответа и время выполнения.

package middleware

import (
    "time"
    "github.com/rs/zerolog/log"
    "net/http"
)

func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
        next.ServeHTTP(wrapped, r)
        log.Info().
            Str("method", r.Method).
            Str("path", r.URL.Path).
            Int("status", wrapped.statusCode).
            Dur("duration", time.Since(start)).
            Msg("HTTP запрос")
    })
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}


                
                

Блоги

Рекомендаций по книгам