Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

slog или zerolog: чем логировать в Go без лишних зависимостей

slog — структурное логирование из стандартной библиотеки: ноль зависимостей, атрибуты и группы, хендлеры, к которым подключаются сторонние движки. zerolog — самый дешёвый по аллокациям JSON-логгер с цепочным API. Совместимость с экосистемой против минимальной цены записи.

slogzerolog

Подбор под ваши приоритеты

Двигайте ползунки — насколько для вас важен каждый критерий (0 — не важен, 5 — критично). Счёт пересчитывается на лету: это взвешенное среднее оценок библиотек по критериям, а не бенчмарк.

Стандартная библиотека против внешнего модуля

Как пишется и читается обычная запись

Аллокации и цена под нагрузкой

Форматы, группы, свои хендлеры

slog-экосистема, библиотеки, адаптеры

Взвешенный счёт

  • slogваш матч76
  • zerolog64

Под ваши веса лидирует slog: библиотекам, cli и сервисам, где важны ноль зависимостей и совместимость с экосистемой.

Развилка «slog или zerolog» возникает в проектах, которые хотят структурные логи, но не тянут тяжёлую конфигурацию: небольшое API, CLI-утилита, воркер в фоне. Оба варианта обещают JSON-строки с полями и минимум церемоний, но подходят с разных сторон. slog появился в стандартной библиотеке с Go 1.21: атрибуты, уровни, группы и хендлеры — без единой внешней зависимости. zerolog живёт в стороннем модуле и десятилетие держит звание самого дешёвого по аллокациям логгера Go. На этой странице сравним их по одним критериям, покажем одну и ту же запись рядом и соберём персональную рекомендацию ползунками под ваши приоритеты.

slog ставит на интерфейс и будущее экосистемы. Запись выглядит как slog.Info("request finished", "path", p, "ms", ms), а чаще — с предвычисленными атрибутами slog.String и slog.Int, которые не боксируются в any. Главный аргумент — контракт: раз логгер живёт в stdlib, сторонние библиотеки принимают *slog.Logger вместо своих типов, контекстные значения прикрепляются через slog.With, а движок подменяется хендлером — сегодня это TextHandler или JSONHandler, завтра любой сторонний. Расплата — производительность: дефолтные хендлеры заметно аллоцируют, JSON-формат скромнее в деталях, а всего изящества церемоний хватило бы и zerolog-у.

zerolog отвечает минимальной ценой записи: лог — цепочка logger.Info().Str("path", p).Int("ms", ms).Msg("done"), JSON собирается руками в байтовый буфер без рефлексии и без аллокаций на горячем пути. Суб-логгеры с полями создаются через With(), конфигурация умещается в три строки, бинарник не раздувается. Расплата — зависимость и интерфейс: zerolog — сторонний модуль в go.mod со своей цепочной лексикой, а для стыковки с библиотеками, которые ждут *slog.Logger, понадобится адаптер hlog или обёртка. Кастомизация формата скромнее: энкодер один, и расширения пишутся руками.

Как читать итог ниже: оценки — усреднение практики команды проекта, а не лабораторные замеры; ползунки подставляют ваши приоритеты. Грубая граница: библиотека, CLI или сервис, где важен ноль зависимостей и совместимость с экосистемой — slog; горячий JSON-конвейер под нагрузкой, где каждая аллокация на счету — zerolog. Если проект уже вырос до тонкой настройки конвейера — посмотрите сравнения zap со slog и zap с zerolog: там разобран третий путь с zapcore-конструктором.

Одна и та же задача в каждой библиотеке

Задача-эталон для этого сравнения: посмотрите на разницу в церемониях, типах и обработке ошибок — так выбор ощущается, а не только описывается словами.

Структурные логи из stdlib: атрибуты, группы и хендлеры без зависимостей

package main

import (
  "log/slog"
  "os"
)

func main() {
  logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

  logger.Info("request finished",
    slog.String("path", "/api/users"),
    slog.Int("ms", 12),
  )
}

Сильные и слабые стороны

slog

Структурные логи из stdlib: атрибуты, группы и хендлеры без зависимостей

  • Ноль зависимостей: logger есть в любом проекте на Go 1.21+ без go.mod-переговоров
  • Контракт экосистемы: сторонние библиотеки принимают *slog.Logger, движок подменяется хендлером
  • Атрибуты и группы: slog.String, slog.Int и With-цепочки без боксирования в any
  • Дефолтные хендлеры аллоцируют: на горячем пути заметно дороже zerolog
  • Цепочек нет: kv-стиль длиннее, чем .Str().Int().Msg() у zerolog

Берите, если: библиотекам, cli и сервисам, где важны ноль зависимостей и совместимость с экосистемой.

zerolog

Самый дешёвый JSON в Go: цепочный API без аллокаций и три строки конфига

  • Цена записи: JSON собирается в буфер без рефлексии — лидер по аллокациям
  • Цепочка .Str().Int().Msg() пишется быстрее, чем читается
  • Суб-логгеры на лету: logger.With().Str("request_id", id).Logger() на каждый запрос
  • Внешний модуль: зависимость в go.mod и своя цепочная лексика
  • Стыковка со slog-экосистемой через адаптер: обёртки hlog или свои

Берите, если: горячим json-конвейерам и небольшим сервисам, где каждая аллокация на счету.

Оценки по критериям

Шкала 1-5: экспертная оценка команды проекта по практике, а не синтетика.

Сравнение slog, zerolog по критериям
Критерийslogzerolog
ЗависимостиСтандартная библиотека против внешнего модуля
Ежедневный кодКак пишется и читается обычная запись
Скорость записиАллокации и цена под нагрузкой
КастомизацияФорматы, группы, свои хендлеры
Стыковка со стекомslog-экосистема, библиотеки, адаптеры

Вердикт: когда что выбирать

Оба логгера закрывают «структурные логи без церемоний», но по-разному: slog даёт контракт стандартной библиотеки и будущую экосистему адаптеров, zerolog — минимальную цену записи и цепочный комфорт. Разумная стратегия: писать приёмники логов под *slog.Logger, а движок выбирать по нагрузке — включая zerolog через адаптер.

Библиотека или пакет, который пойдёт в чужие проектыslogЗависимость из stdlib не добавляет строк в go.mod потребителя
Высокочастотная запись: миллионы событий в минутуzerologJSON без аллокаций держит нагрузку дешевле дефолтных хендлеров
Небольшой сервис без особых требований к логамslogОдин NewJSONHandler — и структура, уровни и поля уже есть
Позже понадобится сэмплинг, секции и тонкий конвейерslog + zap-хендлерДвижок подменяется адаптером без переписывания вызовов

А что выбрали вы?

Кликните по своему выбору — голос сохранится только в вашем браузере (персональные данные никуда не отправляются).

Частые вопросы

Что выбрать — slog или zerolog?

Опирайтесь на две вещи: зависимости и нагрузку. Если проект — библиотека, утилита или сервис, где важно не тянуть внешний модуль и оставаться совместимым с экосистемой — slog из стандартной библиотеки. Если слой логирования горячий — очереди, метрики, миллионы записей — zerolog выигрывает по аллокациям и цене записи. Компромисс тот же, что и с zap: интерфейс slog в коде, движок zerolog под ним через адаптер.

Можно ли zerolog подключить под интерфейс slog?

Да. У zerolog есть пакет hlog, а сообщество поддерживает адаптеры, реализующие slog.Handler поверх zerolog-логгера: вызовы остаются slog.Info и slog.Warn, а форматирование и буферы делает zerolog. Практичный шаблон — принимать *slog.Logger в публичном API пакета и подставлять движок в main: код библиотек не меняется при смене логгера.

Насколько slog медленнее zerolog на самом деле?

На типовом сервисе с сетью и базой разница тонет в IO: десятки наносекунд на запись не видны на фоне миллисекунд запроса. Разница становится заметной на горячих участках — логирование каждого элемента батча, высокочастотные метрики, трейсы: там аллокации дефолтных JSONHandler и TextHandler складываются в ощутимые проценты GC. Замеряйте на своём профиле: если логи не попадают в топ pprof, дефолтного slog достаточно.

Как в slog жить с контекстными полями вроде request_id?

Через With: logger := logger.With(slog.String("request_id", id)) — все последующие записи наследуют поле. В HTTP-мидлваре логгер кладут в context.Context и достают в обработчике через slog.FromContext. У zerolog та же идея реализуется суб-логгером logger.With().Str("request_id", id).Logger(). Оба подхода дают лог записи с корреляцией по запросу без ручного проброса поля в каждый вызов.

Другие сравнения