# Spec Delta ## Purpose Конфигурируемое структурированное логирование сервера: уровень и формат вывода, access-лог запросов с корреляцией по идентификатору и регистрация причин внутренних ошибок. ## ADDED Requirements ### Requirement: Уровень и формат логов Сервер SHALL настраивать уровень и формат логирования из переменных окружения `LOG_LEVEL` (`debug|info|warn|error`) и `LOG_FORMAT` (`json|text`) со значениями по умолчанию `info` и `json` соответственно. #### Scenario: Подробный режим - **WHEN** задан `LOG_LEVEL=debug` - **THEN** выводятся подробные строки, включая access-строки и отказы 4xx #### Scenario: Некорректное значение - **WHEN** `LOG_LEVEL` или `LOG_FORMAT` содержит недопустимое значение - **THEN** сервер не стартует и сообщает явную ошибку конфигурации #### Scenario: Текстовый формат - **WHEN** задан `LOG_FORMAT=text` - **THEN** логи выводятся в текстовом формате ### Requirement: Идентификатор запроса Сервер SHALL присваивать каждому HTTP-запросу идентификатор: принимать валидный `X-Request-ID` либо генерировать новый, SHALL включать его во все логи этого запроса и SHALL возвращать его в заголовке ответа `X-Request-ID`. #### Scenario: Идентификатор передан клиентом - **WHEN** запрос содержит валидный заголовок `X-Request-ID` - **THEN** это значение используется как идентификатор запроса #### Scenario: Идентификатор сгенерирован - **WHEN** заголовок `X-Request-ID` отсутствует или невалиден - **THEN** сервер генерирует новый идентификатор #### Scenario: Эхо в ответе - **WHEN** запрос обработан - **THEN** ответ содержит заголовок `X-Request-ID` с использованным значением ### Requirement: Access-лог запроса На уровне `debug` сервер SHALL писать одну строку на завершённый HTTP-запрос с методом, шаблоном маршрута (без query-строки), статусом, длительностью, объёмом ответа, клиентским IP, идентификатором пользователя (если аутентифицирован) и идентификатором запроса. #### Scenario: Режим debug - **WHEN** задан `LOG_LEVEL=debug` - **THEN** после обработки запроса появляется access-строка #### Scenario: Обычный режим - **WHEN** задан `LOG_LEVEL=info` - **THEN** access-строки не пишутся #### Scenario: Без query-строки - **WHEN** запрос содержит query-параметры - **THEN** query-параметры в access-строку не попадают ### Requirement: Регистрация внутренних ошибок Сервер SHALL логировать причину внутренних ошибок: ошибки, не являющиеся ожидаемыми `AppError`, SHALL писаться на уровне `error` с исходной причиной и идентификатором запроса; ожидаемые отказы со статусом ниже 500 SHALL писаться на уровне `debug`, а со статусом 500 и выше — на уровне `error`. #### Scenario: Внутренняя ошибка - **WHEN** обработка запроса завершается неожиданной ошибкой - **THEN** её причина логируется на уровне `error` вместе с идентификатором запроса #### Scenario: Ожидаемый отказ - **WHEN** запрос отклонён ожидаемой ошибкой со статусом 4xx - **THEN** на уровне `info` причина не логируется, а на уровне `debug` — логируется #### Scenario: Клиентское сообщение не подменяет причину - **WHEN** клиент получает обобщённый ответ о внутренней ошибке - **THEN** в логе сохраняется исходная причина ### Requirement: Фильтрация access-логов Сервер SHALL всегда исключать маршрут `/api/health` из access-логов и SHALL писать access-логи для маршрутов `/api/objects/*` и `/api/sse/*` только на уровне `debug`. #### Scenario: Health исключён - **WHEN** запрошен `/api/health` - **THEN** access-строка не пишется даже на уровне `debug` #### Scenario: Объекты и SSE - **WHEN** запрошен `/api/objects/*` или `/api/sse/*` и задан `LOG_LEVEL=debug` - **THEN** access-строка пишется ### Requirement: Приватность логов Сервер SHALL не записывать в логи query-строку запроса, заголовок `Authorization`, тела запросов, пароли и подписанные URL. #### Scenario: Токен в query - **WHEN** авторизация передана через query-параметр `token` - **THEN** его значение не попадает в логи #### Scenario: Тело запроса не логируется - **WHEN** запрос содержит тело (JSON или multipart) - **THEN** содержимое тела не попадает в логи