Zap / Logrus для Go: полное руководство с примерами

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

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

Начать курс

Введение в логирование в Go: Zap и Logrus

Логирование — это критически важный компонент любого серьёзного приложения. Оно позволяет отслеживать состояние программы, фиксировать ошибки, анализировать производительность и отлаживать код в production-среде. В стандартной библиотеке Go есть пакет log, но он обладает ограниченными возможностями: нет уровней логирования (кроме примитивных), нет структурированного вывода, нет гибких форматов. Для решения этих проблем были созданы две мощные библиотеки: Zap (от Uber) и Logrus (один из первых популярных логгеров).

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

Что такое Zap?

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

Что такое Logrus?

Logrus — это одна из первых и самых популярных библиотек для логирования в Go. Она предоставляет простой API, поддержку уровней, форматтеров (JSON, Text) и хуков (hooks) для интеграции с внешними системами (например, Sentry, Slack). Logrus проще в освоении, но уступает Zap в производительности.

Установка

Установка обеих библиотек выполняется стандартным способом через go get:

// Установка Zap
go get -u go.uber.org/zap

// Установка Logrus
go get -u github.com/sirupsen/logrus

После установки вы можете импортировать их в свой проект:

import (
    "go.uber.org/zap"
    "github.com/sirupsen/logrus"
)

Быстрый старт с примером кода

Пример с Zap

package main

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

func main() {
    // Создаём production-логгер (высокая производительность)
    logger, _ := zap.NewProduction()
    defer logger.Sync() // сбрасываем буфер перед выходом

    // Логируем сообщение с полями
    logger.Info("Приложение запущено",
        zap.String("version", "1.0.0"),
        zap.Int("port", 8080),
    )

    // Используем SugaredLogger для удобства
    sugar := logger.Sugar()
    sugar.Infow("Пользователь вошёл в систему",
        "user", "john_doe",
        "ip", "192.168.1.1",
    )
}

Пример с Logrus

package main

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

func main() {
    // Настраиваем формат вывода (JSON)
    logrus.SetFormatter(&logrus.JSONFormatter{})

    // Устанавливаем уровень логирования
    logrus.SetLevel(logrus.InfoLevel)

    // Логируем сообщение с полями
    logrus.WithFields(logrus.Fields{
        "version": "1.0.0",
        "port":    8080,
    }).Info("Приложение запущено")

    // Логируем ошибку
    logrus.WithFields(logrus.Fields{
        "user": "john_doe",
        "ip":   "192.168.1.1",
    }).Warn("Попытка входа с неверным паролем")
}

Основные методы и функции

1. Zap: NewProduction() / NewDevelopment()

Что делает: Создаёт предварительно настроенный логгер для production или development среды. Production-логгер использует JSON-формат и оптимизирован для скорости. Development-логгер использует цветной текстовый вывод и включает стектрейсы.

Параметры: Нет (возвращает *zap.Logger и ошибку).

Пример:

prodLogger, _ := zap.NewProduction()
devLogger, _ := zap.NewDevelopment()

2. Zap: Logger.Info(), Logger.Warn(), Logger.Error()

Что делает: Логирует сообщение с соответствующим уровнем. Принимает строку сообщения и необязательные поля (через zap.Field).

Параметры: msg string, fields ...zap.Field.

Пример:

logger.Info("Сервер запущен", zap.String("addr", ":8080"))
logger.Error("Ошибка соединения", zap.Error(err))

3. Zap: Sugar() и методы Infow(), Errorw()

Что делает: Преобразует быстрый логгер в "сахарный" (удобный) с поддержкой ключ-значение без явного создания zap.Field. Методы Infow, Errorw принимают чередующиеся ключи и значения.

Параметры: msg string, keysAndValues ...interface{}.

Пример:

sugar := logger.Sugar()
sugar.Infow("Пользователь создан", "id", 42, "name", "Alice")
sugar.Errorw("Не удалось обработать запрос", "status", 500, "error", err)

4. Zap: With()

Что делает: Создаёт дочерний логгер с дополнительными полями, которые будут добавляться к каждому сообщению. Полезно для добавления контекста (например, ID запроса).

Параметры: fields ...zap.Field.

Пример:

requestLogger := logger.With(zap.String("request_id", "abc-123"))
requestLogger.Info("Обработка запроса")
requestLogger.Info("Запрос обработан")
// Оба сообщения будут содержать поле request_id

5. Zap: Named()

Что делает: Создаёт дочерний логгер с указанным именем. Имя добавляется в поле logger в JSON-выводе. Полезно для разделения логов по модулям.

Параметры: name string.

Пример:

dbLogger := logger.Named("database")
dbLogger.Info("Подключение к БД установлено")
// Вывод: {"level":"info","logger":"database","msg":"Подключение к БД установлено"}

6. Logrus: WithFields()

Что делает: Создаёт запись лога с дополнительными полями (структурированные данные). Возвращает *logrus.Entry, который можно использовать для логирования.

Параметры: fields logrus.Fields (map[string]interface{}).

Пример:

logrus.WithFields(logrus.Fields{
    "user": "bob",
    "action": "login",
}).Info("Пользователь вошёл")

7. Logrus: SetFormatter()

Что делает: Устанавливает глобальный формат вывода логов. Доступны JSONFormatter, TextFormatter (по умолчанию). Можно создавать свои форматтеры.

Параметры: formatter logrus.Formatter.

Пример:

logrus.SetFormatter(&logrus.JSONFormatter{
    PrettyPrint: true, // для читаемого вывода
})
logrus.SetFormatter(&logrus.TextFormatter{
    FullTimestamp: true,
})

8. Logrus: SetLevel()

Что делает: Устанавливает минимальный уровень логирования. Сообщения ниже этого уровня будут игнорироваться. Доступные уровни: PanicLevel, FatalLevel, ErrorLevel, WarnLevel, InfoLevel, DebugLevel, TraceLevel.

Параметры: level logrus.Level.

Пример:

logrus.SetLevel(logrus.DebugLevel)
logrus.Debug("Это сообщение отладки")
logrus.Info("Это информационное сообщение")

9. Logrus: AddHook()

Что делает: Добавляет хук (hook) — функцию, которая вызывается для каждой записи лога. Используется для отправки логов во внешние системы (Sentry, Slack, файлы).

Параметры: hook logrus.Hook (интерфейс с методами Levels() и Fire()).

Пример:

type MyHook struct{}

func (h *MyHook) Levels() []logrus.Level {
    return []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel}
}

func (h *MyHook) Fire(entry *logrus.Entry) error {
    // Отправляем entry в Sentry или Slack
    fmt.Println("Хук сработал для уровня:", entry.Level)
    return nil
}

logrus.AddHook(&MyHook{})
logrus.Error("Ошибка, которая будет обработана хуком")

10. Logrus: Entry.WithError()

Что делает: Добавляет ошибку в поля записи лога. Удобно для логирования ошибок с контекстом.

Параметры: err error.

Пример:

err := errors.New("connection refused")
logrus.WithFields(logrus.Fields{
    "service": "api",
}).WithError(err).Error("Не удалось подключиться к БД")

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

Рассмотрим пример микросервиса на Go, который обрабатывает HTTP-запросы и логирует их с помощью Zap. Мы создадим middleware для логирования каждого запроса и отдельный логгер для бизнес-логики.

package main

import (
    "net/http"
    "time"
    "go.uber.org/zap"
)

func main() {
    // Создаём production-логгер
    logger, _ := zap.NewProduction()
    defer logger.Sync()

    // Создаём HTTP-сервер с middleware
    mux := http.NewServeMux()
    mux.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
        // Логируем бизнес-событие
        logger.Info("Получен запрос на /api/user",
            zap.String("method", r.Method),
            zap.String("path", r.URL.Path),
        )

        // Имитация обработки
        time.Sleep(100 * time.Millisecond)
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"status":"ok"}`))
    })

    // Оборачиваем в middleware для логирования всех запросов
    handler := httpLogMiddleware(logger, mux)

    logger.Info("Сервер запущен", zap.String("addr", ":8080"))
    http.ListenAndServe(":8080", handler)
}

// Middleware для логирования HTTP-запросов
func httpLogMiddleware(logger *zap.Logger, next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        // Логируем входящий запрос
        logger.Info("Входящий запрос",
            zap.String("method", r.Method),
            zap.String("path", r.URL.Path),
            zap.String("remote_addr", r.RemoteAddr),
        )

        // Вызываем следующий обработчик
        next.ServeHTTP(w, r)

        // Логируем завершение запроса
        logger.Info("Запрос обработан",
            zap.Duration("duration", time.Since(start)),
            zap.String("method", r.Method),
            zap.String("path", r.URL.Path),
        )
    })
}

Этот пример показывает, как использовать Zap для структурированного логирования в реальном приложении. Вы можете легко ада

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