Логи — это дневник приложения. Каждое событие: пользователь зашёл, форма отправилась, запрос к базе выполнился, ошибка произошла. Если логировать всё подряд без структуры — получится мусор. Если логировать мало — не найдёте причину проблемы.
Хорошее логирование — это не «больше логов», а «правильные логи». Структурированные, с правильными уровнями, доступные для поиска и связи с мониторингом. Разбираем, как это настроить.
Проблемы случаются у всех. Разница в том, как быстро вы их найдёте. С хорошими логами — 5 минут. Без логов — день поиска причины. Логирование напрямую влияет на стоимость поддержки.
1. Уровни логирования
Каждое сообщение имеет уровень важности. Это позволяет фильтровать: на продакшене — только WARN и выше, при отладке — всё до DEBUG.
- DEBUG. Детали для разработки: «получен параметр X = 5», «выполнен запрос к БД за 12 мс». На продакшене почти всегда выключен — слишком много данных.
- INFO. Обычные события: «пользователь вошёл», «заказ оформлен», «письмо отправлено». Помогают восстановить ход событий.
- WARN. Что-то подозрительное, но не сломалось: «внешний API ответил за 4 секунды» (медленно), «пользователь ввёл невалидный email». Обратить внимание.
- ERROR. Что-то сломалось: «не удалось сохранить заказ», «упал запрос к платежному API». Требует реакции, но приложение продолжает работать.
- FATAL. Критическая ошибка, приложение падает. Максимальный приоритет, требует немедленного вмешательства.
2. Structured logging: логи как данные
Классические логи — это строки текста. Их легко читать человеку, но тяжело парсить машине. Если вы хотите искать «все ошибки от пользователя с id=42 за последний час» — со строками это ад.
Structured logging — запись логов в формате JSON. Каждое поле — отдельный атрибут. Можно фильтровать, группировать, строить графики.
Плохо (просто строка):
[2026-09-24 14:32:18] ERROR: Could not process order for user 42. Amount was 5000, but payment failed with code 'insufficient_funds'
Хорошо (structured):
{
"timestamp": "2026-09-24T14:32:18.421Z",
"level": "error",
"event": "order_payment_failed",
"user_id": 42,
"order_id": "ORD-7891",
"amount": 5000,
"currency": "RUB",
"payment_error": "insufficient_funds",
"service": "orders",
"version": "1.4.2"
}
Из этой записи можно делать запросы: «показать все ошибки с payment_error=insufficient_funds», «сколько заказов упало из-за оплаты за неделю», «какая версия сервиса даёт больше всего ошибок». Со строками — всё это невозможно без регулярных выражений.
Как выглядит в коде (Rust):
use tracing::{error, info, warn};
info!(
user_id = 42,
order_id = "ORD-7891",
amount = 5000,
"user_created_order"
);
error!(
user_id = 42,
order_id = "ORD-7891",
payment_error = "insufficient_funds",
"order_payment_failed"
);
Библиотеки structured logging есть в каждом языке: tracing в Rust, structlog в Python, pino в Node.js, monolog в PHP. Они делают одно и то же — превращают логи в структурированные события.
3. Куда писать логи
Есть несколько мест, куда могут идти логи. Каждое подходит для своего сценария.
- Файл. Классика. Пишется в
/var/log/app/app.log. Удобно смотреть черезtail,grep, но плохо масштабируется — на нескольких серверах придётся бегать между ними. - stdout. Современный стандарт для контейнеров (Docker, Kubernetes). Приложение пишет в стандартный вывод, а система логирования (Docker, journald) сама решает, куда это деть. Удобно.
- Syslog / journald. Системный журнал Linux. Хорошо для системных сервисов и критических ошибок. Легко смотреть через
journalctl. - Централизованное хранилище. Loki, Elasticsearch, ClickHouse. Приложение шлёт логи туда по сети. Все сервисы пишут в одно место, и вы ищете по всем сразу. Правильный вариант для серьёзного проекта.
Рекомендация: пишите в stdout, а сбор логов отдайте системе. Это самый гибкий подход — легко подключить любой сборщик, не меняя код приложения.
4. Ротация: как не забить диск
Приложение пишет логи каждую секунду. За сутки накапливаются гигабайты. Через месяц — сотни гигабайт, диск заполнен, сервер упал. Это одна из самых частых причин падения по вине самого разработчика.
Ротация логов через logrotate:
# /etc/logrotate.d/myapp
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
sharedscripts
postrotate
systemctl reload myapp > /dev/null 2>&1 || true
endscript
}
Что это делает:
- Ежедневно переименовывает лог в архив
- Хранит 14 последних архивов (2 недели)
- Сжимает старые логи через gzip (экономия 90%)
- Удаляет самые старые
Настройте ротацию сразу, как только запустите проект. Потом будет поздно — когда диск заполнится, придётся чистить вручную и разбираться, почему всё упало.
5. Централизованный сбор
Как только у вас появляется больше одного сервиса, проблема «зайти на каждый сервер и посмотреть логи» становится критичной. Нужна централизованная система, где все логи собираются в одном месте.
Три популярных варианта:
- Grafana Loki. Лёгкий, простой, дешёвый. Хранит логи в сжатом виде, ищет быстро. Идеален для небольших проектов. Легко ставится через Docker Compose вместе с Grafana.
- ELK (Elasticsearch + Logstash + Kibana). Мощнее, но тяжелее. Elasticsearch жрёт много памяти (минимум 4 ГБ). Для крупных проектов с тысячами логов в секунду.
- ClickHouse + Vector. Современный подход. ClickHouse отлично жмёт и ищет, Vector собирает. Отличное соотношение «мощность / ресурсы».
Пример docker-compose для Loki + Grafana:
version: '3'
services:
loki:
image: grafana/loki:latest
ports: ["3100:3100"]
volumes:
- ./loki-config.yml:/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports: ["3000:3000"]
depends_on: [loki]
environment:
- GF_AUTH_ANONYMOUS_ENABLED=true
Через 10 минут после запуска у вас Grafana на порту 3000, где можно искать по всем логам сразу. Запрос «показать все ERROR за последний час» — одна строка в интерфейсе.
6. Что логировать в веб-приложении
Не «всё подряд», а конкретные события. Вот минимум, без которого отладка превращается в гадание.
- Входящие HTTP-запросы. Метод, URL, статус, время обработки. Есть на уровне веб-сервера (nginx), обычно не нужно дублировать в приложении.
- Бизнес-события. «Пользователь зарегистрировался», «заказ оформлен», «платёж прошёл». По ним видно, работает ли сайт по назначению.
- Ошибки в бизнес-логике. «Не удалось применить промокод», «товар закончился при оформлении». Это не 500-е ошибки, но клиенты их видят.
- Внешние вызовы. Запросы к API платёжной системы, службы доставки, SMS-шлюза. С временем ответа и статусом. Это критично для отладки.
- Медленные запросы. SQL-запросы дольше 500 мс. Помогают находить узкие места.
- Изменения конфигурации. Кто и когда менял настройки. Для аудита.
- Аутентификацию. Успешные и неудачные попытки входа. Важно для безопасности.
7. Что НЕЛЬЗЯ логировать
Логи часто утекают: их копируют, отправляют в поддержку, загружают в сторонние сервисы. Если в логах персональные данные — это уже нарушение 152-ФЗ.
- Пароли. Никогда. Даже хешированные. Восстановление пароля — через ссылку, а не через «мы посмотрели ваш пароль в логах».
- Номера банковских карт, CVV. Категорически. Для отладки достаточно последних 4 цифр карты и первых 6 (BIN).
- Токены и API-ключи. Не только свои, но и клиентские. Если нужно отладить запрос к внешнему API — логируйте факт запроса, но не сам токен.
- Персональные данные в явном виде. Полные паспортные данные, СНИЛС, ИНН. Если нужно — маскируйте:
1234****90. - Тела запросов с данными пользователей. Целые формы, письма, сообщения в чате. Только метаданные: длина, тип, статус.
Даже если сами логи не утекают, их могут увидеть не те люди. Маскируйте персональные данные автоматически: +7 (___) ***-**-42, user_42@***.ru. Тогда даже при случайной утечке ничего критичного не потеряется.
8. Связь с мониторингом
Логи и метрики работают в паре. Метрики показывают «что-то не так» (график ошибок пошёл вверх), логи объясняют «что именно» (вот конкретные ошибки с контекстом).
Идеальная связка: в каждой строке лога есть trace_id — уникальный идентификатор запроса. Метрика показывает проблему, по trace_id находите все логи этого запроса, видите полную картину: какой пользователь, что делал, где упало.
Такой подход называется distributed tracing. Если проект разрастается до микросервисов — без него отладка становится невыносимой. Для монолита достаточно просто добавлять trace_id к каждому запросу.
Логи — это не «для разработчиков». Это инструмент бизнеса. Когда клиент пишет «у меня не работает», вы открываете логи и за минуту находите причину. Когда SEO-трафик падает — по логам видите, какие страницы чаще отдают ошибку. Когда готовите новую фичу — по логам понимаете, какие функции реально используют.
Настроим логирование и мониторинг
Развернём Loki, настроим structured logging, свяжем с алертами в Telegram. От 10 000 ₽.