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

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

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

Начать курс

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

Логирование — это один из краеугольных камней любого production-приложения. Оно помогает отслеживать ошибки, анализировать поведение системы и проводить аудит. В Go, благодаря его простоте и производительности, подход к логированию имеет свои особенности. В этой статье мы разберем лучшие практики логирования в Go (Golang), рассмотрим популярные библиотеки и приведем примеры кода, которые помогут вам построить надежную систему мониторинга.

Почему стандартный пакет log — это не всегда лучший выбор?

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

  • Отсутствие уровней логирования (debug, info, warn, error).
  • Нет поддержки структурированных данных (JSON, key-value).
  • Сложности с настройкой вывода (например, в несколько файлов или в систему сбора логов).
  • Потенциальные проблемы с производительностью при высоких нагрузках.

Для серьезных проектов рекомендуется использовать одну из специализированных библиотек.

Лучшие библиотеки для логирования в Go

На рынке Go-экосистемы есть несколько зарекомендовавших себя библиотек. Рассмотрим три самые популярные:

1. Logrus

Одна из первых библиотек, которая привнесла структурированное логирование в Go. Logrus поддерживает уровни, форматирование (JSON, Text) и хуки для интеграции с внешними сервисами (например, Sentry).

package main

import (
    "github.com/sirupsen/logrus"
)

func main() {
    log := logrus.New()
    log.SetFormatter(&logrus.JSONFormatter{})
    log.SetLevel(logrus.DebugLevel)

    log.WithFields(logrus.Fields{
        "user_id": 123,
        "action": "login",
    }).Info("Пользователь вошел в систему")
}

2. Zap (Uber)

Zap от Uber — это высокопроизводительная библиотека, ориентированная на скорость. Она предлагает два API: SugaredLogger (удобный, чуть медленнее) и Logger (максимальная производительность). Zap идеально подходит для высоконагруженных сервисов.

package main

import (
    "go.uber.org/zap"
)

func main() {
    logger, _ := zap.NewProduction()
    defer logger.Sync() // flushes buffer, if any

    logger.Info("Запрос обработан",
        zap.String("path", "/api/v1/users"),
        zap.Int("status", 200),
        zap.Duration("duration", 0.5),
    )
}

3. Zerolog

Zerolog — еще одна библиотека с фокусом на производительность и минимальное выделение памяти. Она предоставляет fluent API и отличную поддержку JSON-логов "из коробки".

package main

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

func main() {
    zerolog.TimeFieldFormat = zerolog.TimeFormatUnix

    log.Debug().
        Int("user_id", 456).
        Str("role", "admin").
        Msg("Получен запрос на удаление")
}

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

Независимо от выбранной библиотеки, стоит придерживаться следующих правил:

1. Используйте структурированные логи (JSON)

Текстовые логи сложно парсить и анализировать. JSON-формат позволяет легко интегрироваться с системами сбора логов (ELK, Loki, Datadog) и делать сложные запросы.

// Плохо
log.Printf("User %d logged in", userID)

// Хорошо (с Zap)
logger.Info("user logged in", zap.Int("user_id", userID))

2. Правильно выбирайте уровень логирования

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

3. Добавляйте контекст к логам

Всегда включайте в лог идентификаторы (user_id, request_id, trace_id). Это облегчит поиск и анализ.

logger.With(
    zap.String("request_id", reqID),
    zap.String("user_ip", ip),
).Info("Обработка запроса")

4. Настройте ротацию и хранение логов

Если вы пишете логи в файлы, используйте библиотеки для ротации (например, lumberjack). Это предотвратит переполнение диска.

import "gopkg.in/natefinch/lumberjack.v2"

log.SetOutput(&lumberjack.Logger{
    Filename:   "/var/log/myapp/app.log",
    MaxSize:    100, // мегабайт
    MaxBackups: 3,
    MaxAge:     28, // дней
})

5. Избегайте логирования чувствительных данных

Никогда не логируйте пароли, токены, номера кредитных карт или персональные данные (PII). Используйте фильтры или маскировку.

6. Централизованный сбор логов

В production-среде логи должны отправляться в центральную систему (например, Loki, Elasticsearch, CloudWatch). Настройте вывод в stdout/stderr и используйте sidecar-контейнеры (в Kubernetes) или агенты (Fluentd, Filebeat) для сбора.

Пример: Настройка production-логирования с Zap и Lumberjack

Соберем все вместе: высокопроизводительное логирование с ротацией файлов.

package main

import (
    "go.uber.org/zap"
    "go.uber.org/zap/zapcore"
    "gopkg.in/natefinch/lumberjack.v2"
)

func main() {
    // Настройка lumberjack для ротации
    w := zapcore.AddSync(&lumberjack.Logger{
        Filename:   "/var/log/app.log",
        MaxSize:    100, // MB
        MaxBackups: 3,
        MaxAge:     28, // days
    })

    // Конфигурация zap для production
    encoderConfig := zap.NewProductionEncoderConfig()
    encoderConfig.TimeKey = "timestamp"
    encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder

    core := zapcore.NewCore(
        zapcore.NewJSONEncoder(encoderConfig                

Блоги

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