---
title: "Уровни логов: Debug, Info, Warn и Error"
description: "Уровни логирования go в slog: семантика Debug, Info, Warn и Error, что писать на каждом уровне и почему Warn без последействия превращается в шум."
course: "Логирование в Go: структурные логи на slog"
url: "https://golangmaster.ru/courses/slog/lessons/03-urovni-logov/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 18
---

# Уровни логов: Debug, Info, Warn и Error

*Курс «Логирование в Go: структурные логи на slog», урок 3 из 25. ~18 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/slog/lessons/03-urovni-logov/*

Уровни логирования go в slog — четыре ступени важности: Debug, Info, Warn и Error с числами -4, 0, 4 и 8. От того, куда вы поставите запись, зависит, увидит ли её дежурный ночью, сработает ли алерт и можно ли включить подробный вывод без пересборки. В уроке — семантика каждого уровня, что писать на Debug и что на Error, и почему Warn без последействия превращает мониторинг в белый шум.

Два сервиса, одинаковый сбой. Первый логирует всё на 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) отделяет болтливую отладку от полезной.
- Уровень записи не меняется от настроения автора: критерий один — кто обязан её увидеть.

### Четыре вызова и их судьба

```go
func 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 видны везде — подробнее с переменными и безопасными дефолтами разбирается [восемнадцатый урок](/courses/slog/lessons/18-urovni-po-env/). Проверка уровня перед сборкой полей — часть производительности горячего пути: об этом [четырнадцатый урок](/courses/slog/lessons/14-proizvoditelnost/).

### SetLogLoggerLevel: Debug на ходу

```go
// до настройки: запись скрыта, минимум Info
slog.Debug("разбор запроса", "path", "/pay")

slog.SetLogLoggerLevel(slog.LevelDebug)

// теперь запись печатается
slog.Debug("разбор запроса", "path", "/pay")
```

Первый вызов исчезает в тишине, второй — печатает INFO-подобную строку с уровнем DEBUG. SetLogLoggerLevel действует на дефолтный обработчик, завязанный на пакет log; в проекте со своим обработчиком минимум задаётся в HandlerOptions при его создании. Заметьте, что решение о видимости принимает обработчик в момент записи: вызов slog.Debug дешёвый, и его можно оставлять в коде на постоянку.

> **Warn без последействия — это шум.** Кейс: сервис писал 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 при настройках по умолчанию. Что появится в выводе?

- ✅ Только строка Info: Debug скрыт, его уровень ниже минимума
- Обе строки: Debug и Info печатаются по умолчанию
- Только строка Debug: он самый подробный
- Ни одной: без SetDefault логгер не печатает

Минимум дефолтного обработчика — Info (0). Запись Debug имеет уровень -4, обработчик отбрасывает её до форматирования. Поднять видимость можно slog.SetLogLoggerLevel(slog.LevelDebug) — функция появилась в Go 1.22 и меняет минимум дефолтного обработчика на ходу.

### Проверка: какой уровень выбрать

**Вопрос:** Запись в очередь не удалась, но ретрай через секунду прошёл успешно. Какой уровень?

- ✅ Warn: аномалия была, сервис справился сам
- Error: операция завершилась ошибкой
- Debug: сработал ретрай, значит всё в порядке
- Info: успешный итог важнее промежуточных сбоев

Итог благополучный, но аномалия была — это классический Warn: рост частоты таких записей предсказывает Error. Error оставьте ситуации, где операция не выполнена и нужна реакция; Debug подошёл бы, если ретрай считается штатной механикой без единичных сбоев.

### Что выведет программа?

```go
slog.SetLogLoggerLevel(slog.LevelWarn)

slog.Info("готово")
slog.Warn("медленно", "ms", 812)
slog.Error("упало", "id", 42)
```

**Что выведет программа?**

- ✅ Две строки: WARN и ERROR — Info ниже минимума
- Три строки: все уровни выше Debug печатаются
- Одна строка: ERROR — максимум из заданных
- Ничего: минимум Warn отключает и Info, и Error

Минимум Warn (4) пропускает записи с уровнем 4 и выше: Warn и Error печатаются, Info (0) отбрасывается. Обратите внимание, что порог отсекает и обычный ход работы — поэтому Warn в качестве продакшен-минимума выбирают редко, обычно Info.

### Расставьте уровни по смыслу

Допишите вызовы так, чтобы уровень соответствовал содержанию записи.

```go
slog.___("сервис запущен", "port", 8080)
slog.___("медленный запрос", "ms", 812)
slog.___("платёж не прошёл", "id", 42)
```

Подсказка: Нормальный ход — Info, аномалия с самоисправлением — Warn, неудавшаяся операция — Error.

Правильные ответы в пропусках: `Info`, `Warn`, `Error`

### Соберите программу с отладкой

Расставьте строки в порядке выполнения: пакет, импорт, точка входа, включение Debug, отладочная запись, закрывающая скобка.

Правильный порядок строк:

1. `package main`
2. `import "log/slog"`
3. `func main() {`
4. `slog.SetLogLoggerLevel(slog.LevelDebug)`
5. `slog.Debug("разбор запроса", "path", "/pay")`
6. `}`

### Карточки: уровни логов

- **Debug (-4)** — Детали для разработчика: параметры, ветки кода, ответы сервисов. Скрыт по умолчанию, включается без пересборки.
- **Info (0)** — Нормальный ход работы: старт, принятый заказ, смена конфигурации. Лента новостей сервиса, минимум по умолчанию.
- **Warn (4)** — Аномалия, с которой сервис справился сам: ретрай, медленный запрос, приближение к лимиту. Рост частоты предсказывает Error.
- **Error (8)** — Операция не выполнила обещание: платёж не прошёл, запись потеряна. Уровень, по которому срабатывают алерты.
- **slog.SetLogLoggerLevel** — Функция из Go 1.22: меняет минимум дефолтного обработчика на ходу. Для своих обработчиков — опция Level в HandlerOptions.
- **slog.Level** — Числовой тип уровня: Debug -4, Info 0, Warn 4, Error 8. Допустимы промежуточные значения вроде Level(-2).

Уровни разобраны: четыре ступени с числовой шкалой, минимум задаётся обработчиком, Debug скрыт, но доступен на ходу, а Warn работает только при честной семантике. Дальше логично спуститься в сами записи: в [четвёртом уроке](/courses/slog/lessons/04-klyuchi-atributy/) разберём атрибуты — типизированные конструкторы, порядок полей и цену Any; в [одиннадцатом](/courses/slog/lessons/11-oshibki-v-logah/) — как честно класть err в запись уровня Error; в [двадцать третьем](/courses/slog/lessons/23-antipatterny/) — антипаттерны, включая спам уровнями. Алерты по уровню — это уже зона метрик: у нас есть отдельный [курс по Prometheus](/courses/prometheus/). Домашнее задание ниже — ревизия уровней в вашем собственном коде.

## Коротко: что запомнить

- Уровень 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 или панику; связка уровней с завершением процесса вредит тестам и библиотечному коду.
