Есть ли структурированные логи и как они организованы?
Что проверяют
Как у вас реально устроено логирование: можно ли по логам найти конкретный запрос, связать его с трейсом и построить алерт. Следом обычно спрашивают, как request_id проходит через весь запрос и куда логи уходят дальше.
Как отвечать
Если вопрос про ваш проект, опишите его: формат, поля, доставку и поиск. Каркас:
- формат: одна запись это одна строка JSON в stdout. Файлы и ротацию внутри контейнера лучше не держать, логи собирает платформа;
- поля:
timestampв ISO 8601 и UTC,level,logger,eventилиmessage,service,env, версия релиза,request_id,trace_id. Дальше контекст события:user_id,order_id, длительность; - библиотеки:
structlogс процессорами иJSONRenderer, стандартныйloggingсpython-json-loggerилиloguruсserialize=True. Через тот же форматтер должны идти логи uvicorn, gunicorn и библиотек, это настраивается черезlogging.config.dictConfig; - контекст: middleware кладёт
request_idвcontextvars, вstructlogдля этого естьbind_contextvarsиmerge_contextvars. Работает и в asyncio, в отличие от thread-local; - доставка и поиск: Vector, Fluent Bit или Grafana Alloy забирают stdout и отправляют в Loki, Elasticsearch или OpenSearch. С OpenTelemetry по
trace_idиз лога переходите в трейс; - гигиена: исключения через
logger.exception, стек отдельным полем; токены, пароли и персональные данные маскируются до записи.
Где спотыкаются
Собирают JSON руками через json.dumps внутри сообщения и сами подставляют время. Получается строка JSON внутри текстового лога, а стандартные поля и трейсбеки теряются.
Кладут user_id или request_id в метки Loki. Высокая кардинальность раздувает индекс, такие значения живут в теле записи, а метками остаются service, env, level.
Настраивают JSON только для своего логгера. Сторонние библиотеки продолжают писать обычным текстом, и половина логов не парсится.