Журналирование JVM-приложений
#
Васильев Андрей Михайлович, 2024
Версии презентации
Отслеживание работы приложения
#
Серверное приложение обычно запускается на выделенном компьютере и функционирует
постоянно (или согласно графику обслуживания пользователей)
Во время работы приложения могут возникнуть разные ситуации:
- Падение приложения
- Увеличение времени обработки запросов от пользователя
- Окончание свободной оперативной памяти
- Исчерпание свободного пространства на файловой системы
- Невозможность выполнить запрос к СУБД
- Невозможность выполнить HTTP-запрос к нижестоящей службе
Обычно при продуктовом запуске у разработчиков нет возможности физически
подключиться к серверу, чтобы изучить все детали некорректного поведения
приложения
Внешние и внутренние проблемы
#
Выполним классификацию возможных причин критических ситуаций:
- Внутренние причины, обусловленные кодом приложения или используемых библиотек
- Внешние причины, обусловленные состоянием окружения, в котором запущено
приложение
- Доступность ресурсов сервера
- Доступность внешних систем
Системы мониторинга
#
Задача отслеживания состояния серверов в целом решена: создан ряд продуктов,
которые позволяют собрать информацию о состоянии вычислительной системы:
- Объём используемой оперативной памяти
- Объём используемого пространства на файловой системе
- Объём утилизации процессорного времени
- Количество запущенных активных процессов
Данные собираются в единую систему, которая:
- Позволяет визуализировать текущую нагрузку на систему
- Позволяет оповещать эксплуатанта в случае наступления или приближения
критического состояния системы
Архитектура систем мониторинга
#
На каждом компьютере, за которым необходимо следить, запускается агент системы
мониторинга, который собирает информацию и отправляет на центральный узел
Отслеживание состояния приложения
#
Некоторые системы мониторинга позволяют отправлять метрики не только от
собственных агентов, но также и от внешних приложений, например веб-приложений
Каждое приложение обладает важными характеристиками относительно задачи, которую
оно решает, именно их стоит закладывать в метрики, а не обобщённые данные
Общие метрики веб-сервера, запущенного на JVM:
- Состояние виртуальной машины JVM (объём выделенной оперативной памяти, объём
кеша, количество потоков, состояние сборщика мусора, открытые файлы)
- Информация об обработке запроса пользователя:
- Данные о клиенте, выполнившем запрос
- Адрес и параметры запроса от клиента
- Статус ответа
- Время обработки запроса
- Информация о времени выполнении операции над базой данных: время выполнения
операции, объём переданных данных и т.д.
Журналирование работы приложения
#
При отсутствии системы монитроринга можно воспользоваться локальной
альтернативой — журналированием работы приложения в файлы-журналы
Журналы в данном случае являются файлами, расположенными на файловой системе
- Файлы с журналами открываются в момент запуска приложения
- Журналы пишутся всё время работы приложения
Необходимо выполнять ротацию журналов — удаление излишних записей из журнала,
когда всё хранилище заполнено или старые данные уже не актуальны
Журналирование в JVM
#
Ввиду длинной жизни платформы JVM было предложено много решений для выполнения
журналирования внутри приложения:
- Simple Logging Facade for Java 1.x
- Simple Logging Facade for Java 2.x
- Apache Commons Logging
- Logback
- Java Util Logging
Каждый разработчик библиотеки может выбирать любое решение для записи своих
журналов
- Разработчики http4k решили не внедрять систему локального журналирования
- В библиотеку встроена поддержка трассировки запросов в формате Zipkin
- Каждая большая библиотека предоставляет средства интеграции (адаптеры) для
решений, использующие другие решения
Simple Logging Facade for Java 2
#
- Конкретные компоненты приложения зависят от общего фасада slf4j
- Внутри конкретного приложения регистрируются и настраиваются плагины для
вывода журналов на нужные данному приложению пути сохранения данных
Данная библиотека является достаточно гибкой и она используется при разработке Android-приложений, для неё существуют обёртки на языке Kotlin
Формирование сообщений в SLF4J 2
#
Подключение SLF4J к приложению
#
В файле build.gradle.kts
или build.properties.json
необходимо добавить
зависимость от API
Использование библиотеки
#
- В конкретном классе необходимо получить доступ к объекту для записи журнала
- В нужных местах класса добавить вызов методов для записи в журнал
class SomeHandler() : HttpHandler {
private val logger = LoggerFactory.getLogger(SomeHandler::class.java)
override fun invoke(request: Request): Response {
logger.atInfo().log("Обрабатываем очень важный запрос")
}
}
Журналирование обработки запросов
#
Журналирование общей информации по запросам можно доверить фильтру на уровне
всего приложения, т.к. он имеет всю необходимую информацию
val logger = LoggerFactory.getLogger("ru.yarsu.WebApplication")
val loggingFilter = Filter { next: HttpHandler ->
{ request: Request ->
// Собрать данные о запросе
// Вычислить время обработки запроса
val result = next(request)
// Собрать данные об ответе
// Выполнить журналирование информации
logger.atInfo().setMessage("Request")
.addKeyValue("URI", request.uri).log()
result
}
}
- Для получения объекта для записи в журнал используем строковый идентификатор
- Для добавления информации в структурированном виде используем вызов
addKeyValue с передачей пары ключ-значение
Выбор уровня журналирования
#
Библиотеки журналирования предоставляют несколько уровней важности сообщений:
trace, debug, info, warn, error
При записи данных в журнал можно указать желаемый уровень сообщений
Сообщение\Журналирование |
TRACE |
DEBUG |
INFO |
WARN |
ERROR |
OFF |
TRACE |
+ |
- |
- |
- |
- |
- |
DEBUG |
+ |
+ |
- |
- |
- |
- |
INFO |
+ |
+ |
+ |
- |
- |
- |
WARN |
+ |
+ |
+ |
+ |
- |
- |
ERROR |
+ |
+ |
+ |
+ |
+ |
- |
- При работе приложения обычным уровнем журналирования является
INFO
- В случае возникновения ошибки пользователь может расширить уровень
журналирования, чтобы передать расширенный журнал разработчику для исправления
проблемы
Запись журналов в файл с помощью Logback
#
Необходимо добавить зависимость от logback-classic в build.gradle.kts
или
build.properties.json
:
"ch.qos.logback:logback-classic"
В момент запуска приложения Logback пытается выполнить конфигурацию
автоматически:
- Путём загрузки класса реализующего интерфейс Configurator
- Путём загрузки файла
logback-test.scmo
из ресурсов приложения
- Путём загрузки файла
logback.xml
из ресурсов приложения
SCMO-файлы являются бинарным представлением конфигурации, оптимизированы для
ускорения запуска
Пример конфигурационного файла logback.xml
#
- Сохраняем журнал в каталог
logs
, в файл app.log
- Каждый отдельный файл не более 10 мегабайт
- Весь архив не более 100 мегабайт
- Данные кодируются в JSON для обеспечения машинной обработки
- Уровень журналирования информации —
debug
<configuration>
<property name="HOME_LOG" value="logs/app.log"/>
<appender name="FILE-ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${HOME_LOG}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/app.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<!-- Максимальный размер архива журнала 10 МБ -->
<maxFileSize>10MB</maxFileSize>
<!-- Максимальный общий объём архива 100 МБ -->
<totalSizeCap>100MB</totalSizeCap>
<!-- Хранить не более 60 дней -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.JsonEncoder"/>
</appender>
<root level="debug">
<appender-ref ref="FILE-ROLLING"/>
</root>
</configuration>