Статьи

Улучшите ведение журнала в приложении Java EE с tinylog 1.1

tinylog — это легковесная логическая среда для Java. В отличие от Apache Log4j и Logback, tinylog состоит из одного JAR-файла размером всего 80 КБ без каких-либо зависимостей и имеет класс статического регистратора. Это означает, что вам не нужно использовать какой-либо шаблонный код для создания экземпляра регистратора для каждого класса.

1
2
3
public static void main(String[] args) {
   Logger.info("Hello World!"); // Logging methods are static
}

Контекст ведения журнала

Одна из новых возможностей tinylog 1.1 — поддержка контекста журналирования. Эта функция известна как сопоставленный контекст ведения журнала (MDC) из других сред ведения журналов и позволяет на основе потоков обогащать записи журнала дополнительными данными. Например, вы можете установить IP-адрес пользователя для потока один раз, а tinylog будет включать этот IP-адрес во все записи журнала.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
public class HelloWorld extends HttpServlet {
 
   private static final long serialVersionUID = 1L;
 
   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
      LoggingContext.put("ip", request.getRemoteAddr()); // Set IP for this and all client threads
 
      Logger.info("Handle request");
      response.getWriter().append("<h1>Hello World</h1><p>");
      Logger.info("Done request");
   }
 
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
      doGet(request, response);
   }
 
}

Если вы используете балансировщик нагрузки или прокси-сервер, вы получите реальный IP-адрес пользователя, вызвав request.getHeader("X-FORWARDED-FOR"). Контекст ведения журнала должен быть включен в шаблон формата ведения журнала. tinylog можно настроить с помощью файла свойств, системных свойств или Fluent API. Вы можете использовать предпочитаемый способ установки шаблона формата ведения журнала, например, «{date} [{context: ip}] {level}: {message}» для получения следующего вывода:

1
2
2016-05-15 11:40:31 [89.12.191.39] INFO: Handle request
2016-05-15 11:40:31 [89.12.191.39] INFO: Done request

Такой контекст ведения журнала полезен для восстановления: какая запись журнала принадлежит какому запросу. Если у вас есть система входа в систему, имя пользователя является еще одним хорошим кандидатом для ведения журнала контекста. Можно также установить несколько значений. Если вы используете пул потоков, вы не должны забывать очищать контекст журнала при возврате потока в пул потоков.

писатели

tinylog поддерживает несколько писателей для вывода записей журнала. Для приложений Java EE наиболее популярными авторами являются RollingFileWriter и JdbcWriter.

RollingFileWriter может записывать записи журнала в файл. В противоположность основному FileWriter, новый файл журнала может быть запущен после определенных событий. Такими событиями могут быть начало приложения, максимальный размер файла или дата. Определенное количество старых файлов журнала можно сохранить в качестве резервных копий.

Пример для настройки RollingFileWriter через файл свойств:

1
2
3
4
5
tinylog.writer = rollingfile
tinylog.writer.filename = log.txt
tinylog.writer.backups = 10
tinylog.writer.label = count
tinylog.writer.policies = startup, daily

В этом примере RollingFileWriter запускает новый файл журнала при запуске приложения и после каждого дня выполнения. Десять новейших файлов журнала будут сохранены в качестве резервных копий и будут последовательно пронумерованы.
JdbcWriter может записывать записи журнала в базу данных SQL. Это способ централизации журналов, если у вас несколько серверов.
Пример для настройки JdbcWriter через файл свойств:

1
2
3
4
5
6
7
tinylog.writer = jdbc
tinylog.writer.url = jdbc:mysql://localhost/demo
tinylog.writer.table = logs
tinylog.writer.columns = date, ip, level, message
tinylog.writer.values = DATE, CONTEXT, LEVEL, MESSAGE
tinylog.writer.username = demo
tinylog.writer.password = demo

В современных стандартных средах Java и на большинстве серверов веб-приложений JVM может найти драйвер базы данных через службы. К сожалению, автономный Apache Tomcat требует ручной загрузки драйвера базы данных. Это может быть сделано с помощью ServletContextListener.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
@WebListener
public class LifeCycleListener implements ServletContextListener {
 
   @Override
   public void contextInitialized(ServletContextEvent event) {
      try {
         new com.mysql.jdbc.Driver(); // Your database driver
      } catch (SQLException e) {
         Logger.error(e);
      }
    }
 
    @Override
    public void contextDestroyed(ServletContextEvent event) {}
 
}

tinylog 1.2 будет поддерживать источники данных Java EE. Вы должны определить соединение с базой данных только один раз для своего приложения, и вы можете обратиться к источнику данных в вашей конфигурации tinylog. Еще одна новая функция — восстановление соединения с базой данных в случае потери соединения, как только база данных снова заработает.

Написание темы

Из коробки tinylog записывает все записи журнала синхронно. Пишущий поток tinylog может использоваться для выполнения записи в отдельном потоке, чтобы избежать блокирования медленными операциями ввода-вывода. Если активирован, поток записи должен быть отключен вместе с вашим приложением. Это можно сделать, наблюдая за основным потоком или вызывая метод завершения работы. Поскольку в приложениях Java EE нет надежных долгоживущих потоков, предлагаемый способ заключается в использовании ServletContextListener для отключения потока записи.

01
02
03
04
05
06
07
08
09
10
11
12
@WebListener
public class LifeCycleListener implements ServletContextListener {
 
   @Override
   public void contextInitialized(ServletContextEvent event) {}
 
    @Override
    public void contextDestroyed(ServletContextEvent event) {
       Configurator.shutdownWritingThread(false);
    }
 
}

Исходный код

Весь исходный код примера приложения Java EE доступен на GitHub . Дополнительную информацию о tinylog можно найти на сайте tinylog , включая полное руководство.