Содержание урока · 10 разделов
- Уровни логирования go: четыре ступени важности
- Семантика уровней slog
- Что писать на каждом уровне
- Правила уровня простыми словами
- Как включить Debug и не пересобирать бинарник
- Спорное мнение: Error в логе — не про ошибки Go
- Уровни против типизации: важность не равна виду события
- Песочница урока
- Коротко: что запомнить
- Вопросы и ответы
Два сервиса, одинаковый сбой. Первый логирует всё на Error: и неудачный ретрай, и сам сбой, и реакцию на сбой — за ночь алертная цепочка срабатывает сорок раз по одной причине, и дежурный перестаёт открывать уведомления. Второй логирует всё на Info: и сбой тоже — записи видны только тому, кто полез в хранилище руками, а алерт по уровню Error молчит, потому что Error в логе не появлялся ни разу. Оба сервиса честно пишут логи, но уровни выбраны без семантики — и мониторинг либо сгорел, либо слепой. Разберём, на что каждый уровень намекает дежурному и системам алертов, что писать на Debug, а что на Error, и почему уровень — это про важность для читателя, а не про тип события.
Уровни логирования go: четыре ступени важности#
В slog уровень — это число, а не строка: Debug равен -4, Info — нулю, Warn — четырём, Error — восьми. Числовая шкала не украшение: обработчик сравнивает уровень записи со своим минимумом и молча пропускает всё ниже, поэтому Debug скрыт при настройке по умолчанию — ноль против -4. Шкала сортируется: записи можно фильтровать «Warn и выше», а между ступенями разрешены промежуточные значения, например Level(-2) для болтливой отладки, которая важнее Debug, но ещё не Info. Обработчик по умолчанию берёт минимум Info, и slog debug info разделяются сами собой: Info печатается, Debug пропадает. Уровень пишется заглавными буквами в каждой строке вывода — INFO, WARN, ERROR — и по нему же хранилища строят фильтры и дашборды. Главная привычка урока: выбирая уровень, вы выбираете, кто и когда обязан увидеть запись.
Семантика уровней slog#
| Уровень | Число | Когда писать | Пример записи |
|---|---|---|---|
| Debug | -4 | детали для разработчика, поиск причины | разбор запроса: заголовки, шаги, решения |
| Info | 0 | нормальный ход работы сервиса | сервис запущен, заказ принят |
| Warn | 4 | аномалия, сервис справился сам | ретрай подключения, медленный запрос |
| Error | 8 | операция не удалась, нужен человек | платёж не прошёл, запись потеряна |
Что писать на каждом уровне#
Debug — место для всего, что помогает воспроизвести путь выполнения: входящие параметры, выбранная ветка кода, содержимое ответа внешнего сервиса. Правило отбора: если при разборе инцидента вы пожалели, что этого нет в логе, — это Debug. Info — события, из которых складывается история сервиса: старт и остановка, принятые заказы, смена конфигурации. Не гоните сюда отладку — Info читают как ленту новостей. Warn — аномалии, которые сервис обработал сам: первая неудача перед ретраем, медленный запрос, приближение к лимиту. У Warn есть скрытый подписчик — человек и система, которые следят за частотой: рост Warn предсказывает Error. Error — операция не выполнила своё обещание: платёж не прошёл, письмо не ушло, запись потеряна. Пара slog debug info из практики: Debug отвечает на вопрос «как мы сюда попали», Error — на вопрос «что сломалось и что теперь».
Правила уровня простыми словами#
- Debug: включается по необходимости, в проде по умолчанию скрыт — пишите смело.
- Info: событие, о котором стоит узнать постфактум, без действий прямо сейчас.
- Warn: аномалия с самоисправлением; если Warn встречается чаще Error — пересмотрите пороги.
- Error: требуется реакция человека или системы алертов; сюда же — ошибки с потерей данных.
- Промежуточные значения уровня разрешены: Level(-2) отделяет болтливую отладку от полезной.
- Уровень записи не меняется от настроения автора: критерий один — кто обязан её увидеть.
Четыре вызова и их судьба
GOfunc main() {
slog.Debug("разбор запроса", "path", "/pay", "attempt", 2)
slog.Info("сервис запущен", "port", 8080)
slog.Warn("медленный запрос", "ms", 812)
slog.Error("платёж не прошёл", "id", 42)
}При настройках по умолчанию на экране появятся три строки: INFO, WARN и ERROR. Строка Debug пропала молча — её уровень -4 ниже минимума 0, и обработчик отбросил запись до всякого форматирования. Именно поэтому Debug можно писать щедро: в продакшене он не стоит ни памяти на вывод, ни места в хранилище, пока кто-то не поднимет минимум. Числа уровней нужны и системам алертов: правило «Error считается, Warn копится в тренд» выражается одним фильтром по полю level.
Как включить Debug и не пересобирать бинарник#
Вопрос, который приходит сразу: если Debug скрыт, как его увидеть в проде без деплоя? Для обработчика по умолчанию ответ появился в Go 1.22: функция slog.SetLogLoggerLevel меняет минимум дефолтного обработчика на ходу — один вызов из обработчика сигнала или админ-эндпоинта, и Debug начинает печататься. Для собственных обработчиков тот же эффект даёт опция Level в HandlerOptions: минимум читается из переменной окружения или конфига на старте. Схема по окружению, которую чаще всего берут за основу: в разработке минимум Debug, в продакшене Info, а Warn и Error видны везде — подробнее с переменными и безопасными дефолтами разбирается восемнадцатый урок. Проверка уровня перед сборкой полей — часть производительности горячего пути: об этом четырнадцатый урок.
SetLogLoggerLevel: Debug на ходу
GO// до настройки: запись скрыта, минимум Info
slog.Debug("разбор запроса", "path", "/pay")
slog.SetLogLoggerLevel(slog.LevelDebug)
// теперь запись печатается
slog.Debug("разбор запроса", "path", "/pay")Первый вызов исчезает в тишине, второй — печатает INFO-подобную строку с уровнем DEBUG. SetLogLoggerLevel действует на дефолтный обработчик, завязанный на пакет log; в проекте со своим обработчиком минимум задаётся в HandlerOptions при его создании. Заметьте, что решение о видимости принимает обработчик в момент записи: вызов slog.Debug дешёвый, и его можно оставлять в коде на постоянку.
Кейс: сервис писал Warn на каждый 404 и на каждый редкий таймаут стороннего API — сотни строк в час. Когда начался настоящий инцидент, дежурный открыл поток Warn, увидел привычный шум и закрыл его, а настоящая аномалия была ровно в середине. Уровень Warn работает, пока за ним стоит обещание «сервис справился сам, но на это стоит взглянуть». 404 по старой ссылке — это Info или вообще не лог, а чужой таймаут с успешным ретраем — Warn один раз, а не на каждую попытку. Спам на Warn лечится так же, как спам на Error: пересмотром порогов, а не привычкой игнорировать.
Спорное мнение: Error в логе — не про ошибки Go#
Мнение, которое часто путает новичков: Error — это не «в коде вернулась ошибка», а «операция не выполнила обещание, и кто-то должен это починить». Функция возвращает ошибку десятки раз за запрос — упавшее подключение, пустой кэш, отмена контекста — и почти каждая из них штатно обрабатывается. Логировать каждую на Error — значит обесценить уровень за неделю. Пара критериев, по которым ошибка заслуживает Error: эффект необратим для пользователя (платёж не прошёл) или требуется действие за пределами кода (потерялась запись, кончился лимит). Всё, что обработано ретраем, фолбэком или кэшем, — Warn максимум, а чаще Debug. Проверка на ревью простая: если бы Error попал в алерт, был бы он полезен или это просто шум? Тем же вопросом стоит проверять существующий сервис: где Error без последствий — там и место будущего игнора алертов. Ошибки как значения и их оборачивание — за кадром этого курса, они разобраны в курсе по продвинутому Go.
Уровни против типизации: важность не равна виду события#
Распространённая ловушка — использовать уровни как систему типов событий: «заказы логирую на Info, оплаты на Warn, сбои на Error». Уровень говорит о важности для читателя, а не о виде события, поэтому одно и то же событие может пойти на разных уровнях: пользователь отменил заказ — Info, а отмена сорвалась из-за ошибки шлюза — Error. Тип события кодируется сообщением и полями, а не уровнем: заказ оформлен, payment.status=failed — и хранилище спокойно строит диаграммы по полям, не различая уровни. Попытка завести отдельный логгер под каждый вид событий ломает фильтрацию: дежурный ищет «всё, что случилось с заказом 42», а получает четыре ленты. Пара slog debug info показывает правильный разрез: один и тот же разбор запроса на Debug и одно и то же событие на Info — это два уровня значимости, а не два типа событий. Если видов событий становится много, придумывают словарь полей с именами событий — об этом стоит вспомнить, когда дойдёте до полей для сбора.
Единого правильного минимума нет: разработке нужен Debug, продакшену — Info, а тестовый контур иногда живёт с Warn. Поэтому минимум уровня выносят в конфигурацию: переменная окружения на старте, опция Level у обработчика, admin-эндпоинт с SetLogLoggerLevel для срочного включения отладки. Связка «Debug в дев, Info в прод» — самая частая схема, и её детали с безопасными дефолтами — в восемнадцатом уроке курса.
В песочнице сайта log/slog отсутствует — доступно подмножество стандартной библиотеки: fmt, strings, strconv, math, sort, errors, time. Но семантику уровней она разминирует честно: соберите слайс событий с числовыми уровнями, отфильтруйте по минимуму простым сравнением — получите точную модель поведения обработчика: записи ниже порога исчезают до всякого вывода. Логика фильтра — пять строк на fmt, а понимание остаётся на всю жизнь; полный slog запускайте локально через go run.
Проверка: видимость по умолчанию
Программа вызывает slog.Debug и slog.Info при настройках по умолчанию. Что появится в выводе?
Проверка: какой уровень выбрать
Запись в очередь не удалась, но ретрай через секунду прошёл успешно. Какой уровень?
Что выведет программа?
Прочитайте код и выберите, что он выведет:
slog.SetLogLoggerLevel(slog.LevelWarn)
slog.Info("готово")
slog.Warn("медленно", "ms", 812)
slog.Error("упало", "id", 42)Расставьте уровни по смыслу
Допишите вызовы так, чтобы уровень соответствовал содержанию записи.
Соберите программу с отладкой
Расставьте строки в порядке выполнения: пакет, импорт, точка входа, включение Debug, отладочная запись, закрывающая скобка.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: уровни логов
Уровни разобраны: четыре ступени с числовой шкалой, минимум задаётся обработчиком, Debug скрыт, но доступен на ходу, а Warn работает только при честной семантике. Дальше логично спуститься в сами записи: в четвёртом уроке разберём атрибуты — типизированные конструкторы, порядок полей и цену Any; в одиннадцатом — как честно класть err в запись уровня Error; в двадцать третьем — антипаттерны, включая спам уровнями. Алерты по уровню — это уже зона метрик: у нас есть отдельный курс по Prometheus. Домашнее задание ниже — ревизия уровней в вашем собственном коде.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- Уровень slog — число: Debug -4, Info 0, Warn 4, Error 8; обработчик пропускает записи не ниже своего минимума.
- По умолчанию видим Info: Debug скрыт, Warn и Error — сигналы для человека и алертов.
- Debug — как мы сюда попали, Info — нормальный ход, Warn — аномалия с самоисправлением, Error — нужна реакция.
- Warn без последействия превращает поток аномалий в шум: рост Warn предсказывает Error, а не заменяет его.
- Уровень — про важность для читателя, тип события кодируются сообщением и полями.
- Минимум настраивается по окружению: SetLogLoggerLevel для дефолтного обработчика, Level в HandlerOptions для своего.
Домашнее задание
Проведите ревизию уровней в своём проекте: найдите пять записей на Error и ответьте на каждую — требовалась ли реакция человека или хватало Warn. Затем включите Debug в тестовом контуре через slog.SetLogLoggerLevel, повторите один запрос и напишите три поля, которых вам не хватило в Debug-записях для полного разбора.
Личные заметкиПусто
Вопросы и ответы по уроку
Можно ли придумать свой уровень между Warn и Error?
Да: slog.Level — числовой тип, значения вроде Level(6) и Level(-2) легальны, и обработчик сравнивает их обычным числом. На практике это редко нужно: между ступенями обычно достаточно промежуточного Level(-2) для болтливой отладки, а для алертов важнее чистые Warn и Error.
Куда логировать обработанную ошибку — на Warn или Debug?
Зависит от того, насколько она предсказуема: единичный сбой с успешным ретраем — Warn, штатная механика вроде проверки кэша мимо которого — Debug. Критерий один: рост частоты этих записей должен быть сигналом, а не фоном.
Нужен ли отдельный уровень Fatal?
В slog его нет, и это осознанно: фатальный сбой — это Error и решение о завершении процесса, которое принимает код, а не логгер. Логируйте slog.Error, затем os.Exit или панику; связка уровней с завершением процесса вредит тестам и библиотечному коду.
Термины урока
Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.
Изучайте дальше
Следующая ступень учебного пути
PostgreSQL в Go: драйвер pgx с нуля до продакшена
Курс pgx для Go: подключение к PostgreSQL, pgxpool, сканирование структур, транзакции, Batch, COPY, JSONB, LISTEN/NOTIFY и тесты. 25 уроков.
- Подключаться к PostgreSQL через pgx и pgxpool
- Сканировать строки в структуры и справляться с NULL
- Работать с типами Postgres: UUID, JSONB, массивы, timestamptz
- Проводить транзакции и массовые вставки через Batch и COPY