Посібник для розробників Java

Значення та особливості Log4j2 у розробці Java

Ефективне ведення журналів – це невід’ємна частина процесу створення програмного забезпечення. Серед різноманіття Java-фреймворків для журналювання важливо обрати такий, що буде простим у застосуванні, але водночас матиме високу продуктивність, можливості розширення та індивідуалізації. Log4j2 – це безкоштовна бібліотека для Java, яка задовольняє ці критерії.

Інтегруючи Log4j2 у ваш проект, ви отримуєте доступ до таких функцій, як розширена фільтрація, підтримка Java 8 лямбда, пошук властивостей і можливість створювати власні рівні журналювання. Давайте розглянемо, як можна додати Log4j2 до своїх проектів та які її можливості допоможуть вам досягти нових висот у розробці.

Що таке Log4j2?

Ведення журналів є процесом збору корисної інформації (журналів) для подальшого аналізу. Журнали можуть бути використані для швидкого виправлення помилок у коді. Журнали програм допомагають зрозуміти послідовність виконання коду та вирішувати виробничі проблеми та помилки.

Крім діагностики, журнали застосовують для цілей аудиту, наприклад, щоб перевірити, чи було повідомлення успішно доставлено користувачеві.

Log4j2 – одна з найпопулярніших бібліотек для Java, наступник впливової бібліотеки Log4j. Розроблений Apache Software Foundation у рамках Apache Logging Services, Log4j2 є безкоштовним програмним забезпеченням з відкритим кодом (FOSS), що розповсюджується під ліцензією Apache, версія 2.0.

Log4j2 має міцну основу, що походить від оригінального Log4j. Перевага використання Logger перед операторами друку System.out.println() полягає у можливості контролювати, які саме повідомлення відображати, уникаючи зайвих. Належне ведення журналів критично важливе у виробничому середовищі, де відсутні налагоджувачі.

Як додати Log4j2 до вашого проекту?

Існує кілька способів додати Log4j2 до Java-проєкту. Для повноцінного використання всіх можливостей Log4j2 бажано використовувати Java 8 або новішу версію.

Давайте розглянемо різні методи додавання Log4j2 залежно від вимог.

Додавання Log4j2 до проектів з використанням Apache Maven

Якщо ваш проект використовує Apache Maven як систему збірки, необхідно додати залежності Log4j2 у файл pom.xml.

  <dependencies>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.20.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.20.0</version>
    </dependency>
  </dependencies>
  

Для спрощення підтримки єдиної версії для різних артефактів Log4j2 має файл pom.xml зі списком матеріалів (BOM). Якщо ви додаєте його через dependencyManagement, вам не потрібно окремо вказувати версії.

  <!-- Add the BOM to the dependencyManagement -->
    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>org.apache.logging.log4j</groupId>
          <artifactId>log4j-bom</artifactId>
          <version>2.20.0</version>
          <scope>import</scope>
          <type>pom</type>
        </dependency>
      </dependencies>
    </dependencyManagement>

    <!-- Once the BOM is added, the versions are not required -->
    <dependencies>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-api</artifactId>
      </dependency>
      <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
      </dependency>
    </dependencies>
  

Додавання Log4j2 до проектів з використанням Apache Gradle

Якщо ви використовуєте Apache Gradle як інструмент збірки, залежності Log4j2 можна додати до файлу build.gradle.

  dependencies {
    implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
    implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
  }
  

При використанні Gradle версії 5.0 або новішої можна імпортувати специфікацію матеріалів (BOM) Log4j2 Maven, щоб забезпечити узгодженість версій залежностей. Для цього необхідно додати наступне у файл build.gradle.

  dependencies {
    implementation platform('org.apache.logging.log4j:log4j-bom:2.20.0')

    implementation 'org.apache.logging.log4j:log4j-api'
    runtimeOnly 'org.apache.logging.log4j:log4j-core'
  }
  

Для Gradle версій 2.8-4.10 немає прямого імпорту Maven BOM. Вам потрібно додати плагін для функції керування залежностями.

  plugins {
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
  }

  dependencyManagement {
    imports {
      mavenBom 'org.apache.logging.log4j:log4j-bom:2.20.0'
    }
  }

  dependencies {
    implementation 'org.apache.logging.log4j:log4j-api'
    runtimeOnly 'org.apache.logging.log4j:log4j-core'
  }
  

Додавання Log4j2 до автономних програм без інструменту збірки

Якщо проект не має інструменту збірки, необхідну версію артефактів Log4j2 можна завантажити з офіційної сторінки завантаження Log4j2.

Після завантаження необхідно переконатися, що шлях класів вашої програми містить такі JAR-файли:

  • log4j-api-2.20.0.jar
  • log4j-core-2.20.0.jar

Які компоненти входять до складу Log4j2?

Для розуміння особливостей Log4j2 і ефективного використання її можливостей, необхідно зрозуміти, як вона працює. Log4j2 складається з кількох основних елементів. Розглянемо їх по черзі.

#1. LoggerContext

LoggerContext – це центральний елемент системи журналювання, який містить усі реєстратори, запитані в програмі, а також посилання на конфігурацію.

#2. Конфігурація

Конфігурація містить всю необхідну інформацію для системи реєстрації, включаючи реєстратори, додатки, фільтри тощо. У Log4j2 конфігурацію можна визначити за допомогою різних форматів файлів (XML, JSON, YAML) або програмно, через Log4j2 API.

Автоматичне перезавантаження відбувається при кожній зміні будь-якої властивості в конфігурації, тому перезапуск програми не потрібен.

#3. Реєстратор (Logger)

Logger – основний компонент системи Log4j2. Реєстратори отримують у коді програми через оператор LogManager.getLogger() і використовують для генерації журналів. Повідомлення журналу можуть генеруватися на різних рівнях серйозності: налагодження, інформація, попередження, помилка та фатальний.

#4. LoggerConfig

LoggerConfig відповідає за поведінку конкретного Logger. Він визначає налаштування для реєстрації подій, згенерованих цим конкретним реєстратором. Це дозволяє налаштовувати рівні журналювання, додавати аппендери та застосовувати фільтри.

#5. Фільтр

У Log4j2 ви можете вибірково обробляти події журналу за допомогою фільтрів, що застосовуються на основі конкретних критеріїв. Фільтри можна застосовувати до реєстраторів або доповнювачів, контролюючи, які події журналу проходять через конвеєр журналювання для подальшої обробки. За допомогою фільтрів можна точно налаштувати ведення журналів, забезпечуючи обробку лише потрібних журналів.

#6. Доповнювач (Appender)

Appender визначає місце призначення повідомлення журналу. Один реєстратор може мати кілька Appender-ів. Подія журналу буде надіслана всім Appender-ам для цього Logger. Log4j2 має багато попередньо налаштованих додатків. Наприклад, ConsoleAppender реєструє повідомлення на консоль, а FileAppender – у файл. Кожному Appender потрібен макет, що визначає вигляд кінцевого повідомлення журналу.

#7. Макет (Layout)

У Log4j2 макет визначає вигляд кінцевого повідомлення журналу та пов’язаний з Appender-ом. Якщо Appender визначає призначення виводу, то Layout описує, як буде виведено повідомлення.

Топ-5 особливостей Log4j2

Log4j2 має багато функцій, що виділяють її серед інших фреймворків для журналювання Java. Від асинхронних реєстраторів до підтримки лямбда Java 8, Log4j2 має перевагу над іншими. Розглянемо деякі ключові особливості цього фреймворку.

#1. Розширення функціональності за допомогою плагінів

У Log4j 1.x створення розширень вимагало значної зміни коду. Log4j2 вирішує проблему розширюваності, вводячи систему плагінів.

Для оголошення нового плагіну потрібно використати анотацію @Plugin у вашому класі. Завдяки можливостям плагінів можна створювати власні компоненти, такі як фільтри та аппендери. Також можна легко додавати сторонні компоненти.

#2. Підтримка Java 8 Lambda

З виходом Log4j2 версії 2.4 була введена підтримка лямбда-виразів Java 8. Лямбда-вирази дозволяють визначати логіку ведення журналу вбудовано. Це зменшує потребу у багаторядкових перевірках або анонімних внутрішніх класах. Також це гарантує, що ресурсомісткі методи не будуть виконуватися без потреби. В результаті код стає чистішим, легшим для читання, а системні накладні витрати зменшуються.

Розглянемо приклад, коли ви реєструєте результат ресурсомісткої операції, але тільки якщо ввімкнено рівень налагодження. До підтримки лямбда-виразів це робили так:

  if (logger.isDebugEnabled()) {
    logger.debug("The output of the given operation is: {}", expensiveOperation());
  }
  

Наявність кількох таких випадків використання створить зайві умовні перевірки. За допомогою Log4j2 ту саму дію можна виконати так:

  logger.debug("The output of the given operation is: {}", () -> expensiveOperation()
  

Метод exprensiveOperation() обчислюється тільки якщо рівень налагодження ввімкнений. Немає потреби у явних перевірках.

#3. Асинхронні реєстратори

Кожна подія журналу є операцією введення-виведення, що збільшує накладні витрати на систему. Щоб зменшити їх, Log4j2 представляє асинхронні реєстратори, які працюють у окремому потоці від потоку програми. Під час використання асинхронних реєстраторів, потік, що викликає, негайно отримує назад контроль після виклику методу logger.log().

Це дозволяє йому продовжувати роботу з логікою програми, не очікуючи завершення реєстрації події. Завдяки асинхронній поведінці досягається більша пропускна здатність журналювання. Можна зробити всі реєстратори асинхронними за замовчуванням або комбінувати синхронну й асинхронну поведінку.

#4. Журналювання без сміття

У Java збирання сміття – це процес автоматичного очищення невикористаних об’єктів в програмі. Хоча ця операція відбувається автоматично, збирання сміття має накладні витрати.

Якщо ваша програма створює занадто багато об’єктів за короткий час, збирання сміття може зайняти більше системних ресурсів, ніж потрібно. Деякі бібліотеки журналювання, зокрема попередні версії Log4j, створюють багато тимчасових об’єктів під час процесу журналювання. Згодом тиск на збирач сміття впливає на продуктивність системи.

Починаючи з версії 2.6, Log4j2 працює в режимі «без сміття». Це типова поведінка. Об’єкти використовуються повторно, а створення тимчасових об’єктів значно скорочується.

Наступні зображення показують, як Log4j2 версії 2.6 зменшує проблему непотрібних об’єктів порівняно з Log4j2 версії 2.5.

У Log4j2 версії 2.5 під час процесу журналювання створюється багато тимчасових об’єктів; Джерело: apache.org

У Log4j2.6 тимчасові об’єкти не створюються під час процесу журналювання; Джерело: apache.org

#5. Пошуки

У Log4j2 можна додавати контекстну інформацію до своїх журналів за допомогою пошуків. Завдяки їм ви можете додавати дані з різних джерел, таких як властивості системи, змінні середовища або користувацькі значення. Це дозволяє включати відповідну динамічно витягнуту інформацію, що робить журнали більш корисними.

Розглянемо приклад, коли ви хочете зареєструвати ідентифікатор сеансу користувача у всіх рядках журналу, щоб можна було потім знайти всі журнали, що відповідають ідентифікатору сеансу.

Безпосереднім способом зробити це було б додавати ідентифікатор сеансу явно, що стає важким у підтримці. Згодом ви можете забути додати його, втративши цінну інформацію.

  logger.info("The user data has been fetched for session id {}", sessionId);
  ...
  logger.info("The transaction has been processed for session id {}", sessionId);
  ...
  logger.info("Request has been successfully processed for session id {}", sessionId);
  

Кращим рішенням буде скористатися пошуком контекстної карти. Ідентифікатор сеансу можна додати до контексту потоку в коді програми. Потім це значення можна використати в конфігурації Log4j2. Це усуває потребу явного згадування його в повідомленнях журналу.

  ThreadContext.put("sessionId", sessionId);
  

Після додавання значення його можна використати у Lookup за допомогою ключового слова ctx.

  <File name="Application" fileName="application.log">
    <PatternLayout>
      <pattern>%d %p %c{1.} [%t] $${ctx:sessionId} %m%n</pattern>
    </PatternLayout>
  </File>
  

Як створити власні рівні журналу в Log4j2?

Рівні журналу в Log4j2 класифікують події журналу на основі їх серйозності або важливості. Ви можете контролювати рівень журналу при реєстрації повідомлення в коді програми.

Наприклад, logger.debug() встановлює рівень DEBUG. Відповідно, logger.error() встановлює рівень ERROR. Це визначає, які повідомлення відображатимуться у виводі. Рівень журналу можна налаштувати у файлі конфігурації.

Попередньо налаштовані рівні журналу в Log4j2 та їхні відповідні значення наведені нижче:

OFF 0 FATAL 100 ERROR 200 WARN 300 INFO 400 DEBUG 500 TRACE 600 ALL МАКС. ЗНАЧЕННЯ

Якщо для рівня журналу встановлено певне значення, виводяться всі рядки журналу для цього значення і рядки вище (з меншим значенням). Інші ігноруються.

Наприклад, якщо ви встановите рівень журналу як WARN, відображатимуться повідомлення WARN, ERROR і FATAL. Будь-який інший рядок журналу буде проігноровано. Це особливо корисно при запуску одного коду в різних середовищах.

Ви можете встановити рівень журналу на INFO або DEBUG при запуску коду у вашому середовищі розробки. Це дасть змогу бачити більше журналів та допомагати у процесі розробки. Але під час роботи у виробничому середовищі бажано встановити значення ERROR. Так ви зможете зосередитися на проблемах у разі виникнення будь-якої аномалії та не переглядати зайві рядки журналу.

Іноді може виникнути потреба додати власний рівень журналу на додаток до попередньо налаштованих. Log4j2 дозволяє це легко зробити. Розглянемо, як можна додати власні рівні журналу та використовувати їх у програмі.

#1. Додавання спеціального рівня журналу через файл конфігурації

Додати власні рівні журналу можна, оголосивши їх у файлі конфігурації.

У прикладі нижче визначено настроюваний рівень журналу під назвою NOTICE зі значенням 450. Він знаходиться між INFO (400) та DEBUG (500). Це означає, що якщо рівень NOTICE встановлено, то повідомлення INFO реєструватимуться, а повідомлення DEBUG – ні.

  <?xml version="1.0" encoding="UTF-8"?>
  <Configuration>
    <CustomLevels>
      <CustomLevel name="NOTICE" intLevel="450" />
    </CustomLevels>
   
    <Appenders>
      <File name="MyFile" fileName="logs/app.log">
        <PatternLayout pattern="%d %-7level %logger{36} - %msg%n"/>
      </File>
    </Appenders>
    <Loggers>
      <Root level="trace">
        <AppenderRef ref="MyFile" level="NOTICE" />
      </Root>
    </Loggers>
  </Configuration>
  

#2. Додавання спеціального рівня журналу в коді

Окрім оголошення їх у файлі конфігурації, можна визначити власні рівні журналу в коді.

  final Level VERBOSE = Level.forName("VERBOSE", 550);
  

Це створить новий рівень журналу під назвою VERBOSE. Він знаходитиметься між DEBUG (500) та TRACE (600). Якщо для реєстратора встановлено рівень VERBOSE, реєструватимуться всі повідомлення журналу VERBOSE і вище, включаючи DEBUG. Повідомлення TRACE будуть пропущені.

#3. Використання спеціального рівня журналу в коді

Перед використанням власних рівнів журналу їх потрібно оголосити. Це можна зробити у файлі конфігурації або коді. Після оголошення їх можна використовувати.

У цьому прикладі показано, як можна оголосити спеціальний рівень під назвою NOTICE та використовувати його.

  final Level NOTICE = Level.forName("NOTICE", 550);

  final Logger logger = LogManager.getLogger();
  logger.log(NOTICE, "a notice level message");
  

Хоча це створить потрібне повідомлення з новоствореним рівнем, постійно передавати рівень явно може бути складно. На щастя, ви можете створити допоміжні методи для реєстрації ваших користувацьких рівнів. Так можна буде використовувати свій метод logger.notice(), подібно до logger.debug() або logger.error().

Log4j2 постачається з утилітою, що допомагає створювати власні розширені журнали. Наступна команда створює файл Java під назвою CustomLogger.java. Цей файл містить наявні методи журналу, а також нові методи для рівня NOTICE.

  java -cp log4j-core-2.20.0.jar org.apache.logging.log4j.core.tools.ExtendedLoggerGenerator 
          com.example.CustomLogger NOTICE=450 > com/example/CustomLogger.java
  

Після створення файлу можна використовувати клас у коді для створення нових реєстраторів. Ці реєстратори міститимуть додаткові методи для вашого спеціального рівня журналу. Так ви можете розширити функціональність своїх реєстраторів.

  final Logger logger = CustomLogger.create(ValueFirstSmsSender.class);

  //this new method is similar to using logger.debug()
  logger.notice("a notice level message");
  

Висновок

Log4j2 – це потужний фреймворк для журналювання Java з широким набором функцій, конфігурацій та вдосконалень продуктивності. Оскільки журнали є важливою частиною процесу розробки програмного забезпечення, наявність надійного фреймворку, як Log4j2, покращує можливості програми.

Гнучкість і розширюваність Log4j2 дозволяють фіксувати події, що відбуваються у вашій програмі. Це дає можливість використовувати журнали як потужний інструмент для налагодження та аудиту. Завдяки своїм функціям і вдосконаленням Log4j2 є гарним вибором для різноманітних програмних проектів.

Можливо, вас також зацікавлять ці Java IDE та онлайн-компілятори.