- internal/logging: LOG_LEVEL/LOG_FORMAT parsing and slog handler - request_id middleware (X-Request-ID or generated, echoed) + debug access log - httpx registers errors; middleware logs internal causes with request_id - health excluded, objects/sse debug-only, no query/body/secrets in logs - new observability capability spec; README/.env.example docs
6.1 KiB
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 содержимое тела не попадает в логи