Как парсить логи для мониторинга
Что проверяют
Понимаете ли вы, что логи для мониторинга не разбирают самописным кодом, а с самого начала пишут в удобном для разбора виде и отдают в систему сбора. Следом обычно спрашивают, чем логи отличаются от метрик и как связать строки одного запроса между сервисами.
Как отвечать
Начните с источника: лучший парсинг тот, которого нет. В Java это структурированные логи в JSON:
- Logback: энкодер
logstash-logback-encoder, каждая запись становится JSON-объектом с полями времени, уровня, логгера, сообщения и стектрейса; - Log4j2:
JsonTemplateLayoutс готовыми шаблонами, например под ECS; - Spring Boot: начиная с 3.4 есть встроенные структурированные логи, формат задаётся свойством
logging.structured.format.console(ecs,logstash,gelf); - MDC: в контекст кладут
traceId, идентификатор пользователя, номер заказа. Micrometer Tracing проставляетtraceIdсам, и по нему собираются все строки одного запроса.
Дальше конвейер: приложение пишет в stdout, агент (Fluent Bit, Vector, Filebeat) забирает, хранилище (Loki, Elasticsearch или OpenSearch) индексирует, Grafana или Kibana показывает и шлёт алерты. Старые текстовые логи разбирают на стороне агента или хранилища: grok-шаблоны в Logstash, | pattern и | regexp в LogQL.
Если разбирать всё же приходится в Java, читайте потоком через Files.lines или BufferedReader, а регулярку компилируйте один раз в Pattern, а не через String.matches в цикле.
И разведите роли: число ошибок, время ответа, загрузку пула соединений считают метриками через Micrometer и Prometheus, а логи нужны, чтобы понять причину конкретного инцидента.
Где спотыкаются
Ищут ошибки через line.contains("ERROR"). Это ловит слово в тексте сообщения и режет стектрейс, который занимает несколько строк, на отдельные куски. JSON решает обе проблемы.
Выносят userId или requestId в метки Loki. Высокая кардинальность раздувает индекс, такие поля должны оставаться в теле записи.
Пишут в логи токены, пароли и персональные данные. Маскировать нужно в момент записи, фильтр в хранилище срабатывает слишком поздно.