IT в 2026: когда хайп заканчивается и начинается инженерия
Почему продакшен без логирования и метрик превращается в хаос при инциденте. Что логировать, куда писать, как искать по логам — конфиги, команды и типичные ошибки.
Требования
- Доступ к серверу или контейнерам
- Базовое понимание логов и метрик
IT в 2026: когда хайп заканчивается и начинается инженерия
Запрос логирование production или инцидент нельзя воспроизвести часто упирается в одну и ту же проблему: в проде что-то падает или ведёт себя странно, а разобрать происходящее нельзя — логов нет, метрик нет, запрос нельзя привязать к конкретному пользователю или шагу. Статья про одну задачу: почему продакшен без нормального логирования и минимального мониторинга превращается в хаос при инциденте и что сделать практически: что логировать, куда писать, как искать по логам (с примерами конфигов и команд). Без философии — только инженерный разбор и код. Для автоматизации и фона на сервере пригодится Backend-автоматизация: cron, агенты; для выката и тестов — CI/CD для PHP, Bitrix и Laravel: GitHub Actions.
В чём проблема
Симптомы:
- Пользователь или поддержка сообщают об ошибке («заказ не оформился», «страница не открывается»). Воспроизвести не получается — нет ни времени запроса, ни ID пользователя, ни трейса.
- В логах веб-сервера или приложения за нужный период пусто или одна строка без контекста; нельзя связать запрос с действием пользователя.
- После инцидента разбор занимает часы: непонятно, на каком сервере, в каком контейнере и в какой момент всё сломалось.
Пример типичного «пустого» лога: в error.log только уровень и сообщение, без request_id, без timestamp в едином формате, без ссылки на один и тот же запрос в nginx и приложении:
[error] PHP Fatal error: Call to a member function ... on null
По такому сообщению нельзя быстро найти тот самый запрос среди тысяч других.
Почему возникает:
- Нет единого идентификатора запроса (request_id / trace_id) — логи nginx, PHP и приложения не связаны между собой, нельзя собрать одну историю запроса.
- Логи только в stdout без сохранения или ротации — в Docker контейнере логи уходят в stdout и при перезапуске теряются; на сервере файл логов растёт без ротации и заполняет диск.
- Не логируются критические параметры — нет user_id, order_id, ключевых полей запроса; нельзя отфильтровать логи по одному заказу или пользователю.
- Нет минимальных метрик — нельзя быстро увидеть всплеск ошибок, рост времени ответа или падение доступности без разбора сырых логов.
Итог: одна статья = одна задача — настроить такое логирование и минимальный поиск по логам, чтобы инцидент можно было разобрать за минуты, а не часы.
Как понять, что логов не хватает: если при обращении пользователя «что-то сломалось в 14:00» вы не можете за пару минут вытащить все записи по его запросу (nginx + приложение) и увидеть стек ошибки или параметры запроса — логирование недостаточное. Достаточный минимум: в каждой строке лога приложения есть request_id, время в едином формате и хотя бы один контекстный идентификатор (user_id, order_id или URL). Без этого разбор инцидента превращается в перебор логов и догадки — в 2026 году такой подход не масштабируется ни по трафику, ни по количеству сервисов. Достаточно один раз настроить формат с request_id и ротацию; дальше каждый инцидент разбирается в разы быстрее.
Рабочее решение
Стек: веб-сервер (Nginx или аналог), PHP/приложение, доступ к файловой системе или централизованному сбору логов. Среда: prod, Docker, VM — принцип один: один запрос = один идентификатор, все логи с ним связаны.
1) Единый формат строки лога и request_id
Каждая строка лога должна содержать хотя бы: timestamp (ISO 8601), уровень, request_id, сообщение. Тогда по одному request_id можно собрать все записи одного запроса.
Пример формата (JSON для удобного парсинга):
{"time":"2026-02-22T12:00:01.000Z","level":"error","request_id":"a1b2c3d4","msg":"Order save failed","user_id":42,"order_id":null}
В приложении генерируйте request_id в начале обработки запроса (middleware или аналог) и пробрасывайте его во все вызовы логирования. В Nginx можно прокидывать заголовок X-Request-ID из приложения или генерировать на уровне балансера.
2) Nginx — логировать request_id и время
Файл конфигурации (фрагмент): например, /etc/nginx/conf.d/log_format.conf или внутри server {}
# Добавить в http {} или в server {}
log_format main_with_request_id '$remote_addr - $remote_user [$time_iso8601] '
'"$request" $status $body_bytes_sent '
'"$http_x_request_id" "$http_referer" "$http_user_agent"';
access_log /var/log/nginx/access.log main_with_request_id;
error_log /var/log/nginx/error.log warn;
Если приложение отдаёт заголовок X-Request-ID, он попадёт в $http_x_request_id и будет в каждой строке access-лога. По нему можно связать запрос в Nginx и в логах приложения.
3) PHP — писать request_id и контекст в лог
Пример минимального логгера, который дополняет сообщение request_id и контекстом (user_id, order_id):
<?php
function app_log(string $level, string $message, array $context = []): void
{
$requestId = $_SERVER['HTTP_X_REQUEST_ID'] ?? bin2hex(random_bytes(8));
$line = json_encode([
'time' => date('c'),
'level' => $level,
'request_id' => $requestId,
'msg' => $message,
...$context,
], JSON_UNESCAPED_UNICODE) . "\n";
file_put_contents('/var/log/app/app.log', $line, FILE_APPEND | LOCK_EX);
}
// Использование при ошибке сохранения заказа
app_log('error', 'Order save failed', ['user_id' => $userId, 'order_id' => $orderId]);
В продакшене лучше писать в stdout (Docker) или в файл с ротацией (logrotate), а не в один файл без ротации. Важно: один и тот же request_id должен быть в Nginx и в приложении — для этого приложение должно читать X-Request-ID из входящего запроса или выставлять его в ответе, а Nginx — логировать.
4) Поиск по логам за один запрос
Когда у вас есть request_id от пользователя или из тикета, можно собрать все строки по нему:
grep 'a1b2c3d4' /var/log/nginx/access.log
grep 'a1b2c3d4' /var/log/app/app.log
Если логи в JSON:
grep 'a1b2c3d4' /var/log/app/app.log | jq .
По времени инцидента — сузить выборку по минутам:
grep '2026-02-22T12:00' /var/log/app/app.log
Эти команды дают диагностическую ценность: за одну минуту можно увидеть полную картину по одному запросу.
5) Ротация логов (logrotate)
Чтобы логи не забили диск и старые данные не терялись бесконтрольно:
Файл: /etc/logrotate.d/app
/var/log/app/app.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
}
После настройки проверьте: logrotate -d /etc/logrotate.d/app (dry-run).
Проверка результата
1. Сгенерировать запрос с request_id
Сделайте запрос к приложению (браузер или curl). В ответах или в конфиге Nginx убедитесь, что заголовок X-Request-ID присутствует (или генерируется на уровне Nginx). Откройте access.log и найдите последнюю строку с этим request_id.
Команда:
tail -n 1 /var/log/nginx/access.log
Ожидаемый вывод: строка, в которой есть тот же идентификатор, что и в логах приложения (если приложение логирует его). Для приложения:
tail -n 5 /var/log/app/app.log
В строках должны быть request_id, time, level, msg и при необходимости user_id/order_id.
2. Поиск по request_id
Подставьте реальный request_id из лога:
grep 'REQUEST_ID_HERE' /var/log/nginx/access.log /var/log/app/app.log
Ожидаемый результат: несколько строк из разных файлов с одним и тем же request_id — значит, запрос можно проследить от входа до приложения.
Типичные ошибки
1. Не использовать request_id — без него нельзя связать одну ошибку в приложении с конкретным HTTP-запросом. Добавьте генерацию или проброс X-Request-ID и пишите его в каждую строку лога приложения.
2. Логи только в stdout без сохранения в Docker — при перезапуске контейнера всё теряется. Либо пишите в volume (файл внутри контейнера, примонтированный с хоста), либо отдавайте stdout в централизованный сбор (Graylog, Loki, облачный лог-сервис) и не полагайтесь только на docker logs.
3. Один огромный файл логов без ротации — диск заполняется, поиск замедляется. Настройте logrotate (или аналог) и ограничьте срок хранения (например, 14 дней), как в примере выше.
4. Логировать только уровень и текст ошибки — без user_id, order_id, request_id разбор инцидента превращается в угадывание. Добавьте минимальный контекст (идентификаторы запроса, пользователя, сущности) в каждую критичную запись.
Если логов много и поиск медленный: используйте ротацию (logrotate) и ограничьте срок хранения. Для поиска по дате и времени держите единый формат timestamp (ISO 8601) во всех логах. При росте объёма рассмотрите централизованный сбор (Loki, Graylog, облачный лог-сервис) с индексацией по request_id и времени — тогда поиск по одному запросу остаётся быстрым даже при гигабайтах логов.
Где применять
- Prod — основная среда: без логов и request_id разбор инцидентов неэффективен. Внедрите формат и ротацию на всех инстансах, которые обрабатывают трафик.
- Docker — убедитесь, что логи приложения пишутся в stdout или в примонтированный volume; при необходимости настройте драйвер логирования контейнера и сбор в одну систему.
- CI/CD — в пайплайне можно проверять наличие полей в логах (например, что request_id присутствует в каждой строке) или запускать тесты, которые пишут в лог и проверяют вывод. Пример настройки пайплайнов: CI/CD и GitHub Actions: workflow, secrets, тесты и деплой.
Итог: хайп заканчивается там, где начинается ответственность за воспроизводимость и разбор инцидентов. Одна практическая привычка — нормальное логирование с request_id и контекстом — сильно сокращает время на разбор проблем в 2026 и дальше.



Комментарии