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

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

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

Начать курс

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

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

Почему логирование важно в Go?

Go — язык для создания высоконагруженных серверных приложений. Без качественного логирования вы не сможете:

  • Быстро локализовать баги в production-среде
  • Понять последовательность событий при сбое
  • Провести аудит безопасности
  • Оптимизировать производительность

Хорошее логирование должно быть: структурированным, уровнем (debug, info, warn, error), контекстным (содержать ID запроса, имя функции) и управляемым (ротация, вывод в файл).

1. Начинаем со стандартной библиотеки log

Go предоставляет встроенный пакет log — простой и достаточный для небольших проектов. Вот как он работает:

package main

import (
    "log"
    "os"
)

func main() {
    // Базовая настройка: вывод в stderr
    log.SetOutput(os.Stderr)
    log.SetPrefix("INFO: ")
    log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)

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

Ключевые возможности log:

  • log.Println() — вывод строки
  • log.Printf() — форматированный вывод
  • log.Fatal() — вывод + завершение программы
  • log.Panic() — вывод + паника

Но у стандартного логгера есть недостатки: нет уровней (debug, info, error), нет структурированного вывода (JSON), нет ротации файлов.

2. Добавляем уровни логирования

Для production-приложений критически важно разделять логи по уровням. Вот минимальная реализация с использованием стандартного пакета:

package main

import (
    "log"
    "os"
)

type Logger struct {
    debug *log.Logger
    info  *log.Logger
    warn  *log.Logger
    error *log.Logger
}

func NewLogger() *Logger {
    flags := log.Ldate | log.Ltime | log.Lshortfile
    return &Logger{
        debug: log.New(os.Stdout, "DEBUG: ", flags),
        info:  log.New(os.Stdout, "INFO: ", flags),
        warn:  log.New(os.Stderr, "WARN: ", flags),
        error: log.New(os.Stderr, "ERROR: ", flags),
    }
}

func (l *Logger) Debug(msg string, args ...interface{}) {
    l.debug.Printf(msg, args...)
}

func (l *Logger) Info(msg string, args ...interface{}) {
    l.info.Printf(msg, args...)
}

func (l *Logger) Error(msg string, args ...interface{}) {
    l.error.Printf(msg, args...)
}

func main() {
    log := NewLogger()
    log.Info("Сервер запущен на порту %d", 8080)
    log.Debug("Запрос от %s", "192.168.1.1")
    log.Error("Не удалось подключиться к БД: %v", "timeout")
}

Совет: В production используйте os.Stderr для ошибок и os.Stdout для обычных логов — это упрощает сбор логов через systemd или Docker.

3. Структурированное логирование (JSON)

В современных системах логи часто собираются в централизованные хранилища (Elasticsearch, Loki). Для этого нужен структурированный вывод, например в JSON. Популярные библиотеки:

  • logrus — гибкий, с поддержкой форматтеров
  • zap — сверхбыстрый, от Uber
  • zerolog — легковесный и быстрый

Пример с logrus:

package main

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

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

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

    log.WithField("error", "connection refused").Error("Ошибка подключения к БД")
}

Преимущества JSON-логов:

  • Машиночитаемый формат — легко парсить
  • Поддержка вложенных полей и метаданных
  • Совместимость с ELK-стеком и Grafana Loki

Пример вывода:

{"action":"login","ip":"10.0.0.1","level":"info","msg":"Пользователь вошел в систему","time":"2025-03-15T10:30:00Z","user_id":12345}

4. Ротация логов и работа с файлами

Если вы пишете логи в файл, без ротации файл будет расти бесконечно. Лучшие практики:

  • Используйте lumberjack для автоматической ротации
  • Настройте максимальный размер файла (например, 100MB)
  • Установите количество архивов (например, 7)
  • Не забывайте про сжатие старых логов

Пример с lumberjack и logrus:

package main

import (
    "github.com/sirupsen/logrus"
    "gopkg.in/natefinch/lumberjack.v2"
)

func main() {
    log := logrus.New()

    // Настройка ротации
    log.SetOutput(&lumberjack.Logger{
        Filename:   "/var/log/myapp/app.log",
        MaxSize:    100, // мегабайт
        MaxBackups: 7,   // количество архивов
        MaxAge:     30,  // дней
        Compress:   true,
    })

    log.Info("Логи будут автоматически ротироваться")
    log.Warn("Файл не превысит 100MB")
}

Важно: В контейнерах (Docker) обычно выводят логи в stdout/st

Блоги

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