Водич за Јава програмере

Кључни аспект у процесу развоја софтвера је адекватно логовање. Са бројним доступним Java framework-ovima за логовање, битно је изабрати онај који је једноставан за употребу. У исто време, одабрани framework треба да буде ефикасан, прилагодљив и да омогућава персонализацију. Log4j2 је бесплатна Java библиотека за логовање која испуњава све наведене критеријуме.

Интеграција Log4j2 са било којом апликацијом омогућава опције као што су унапређено филтрирање, подршка за Java 8 lambda изразе, претраживање својстава и прилагођени нивои логовања. Погледајмо како можете додати 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 пројекат. Препоручљиво је користити Java 8 или новију верзију да бисте користили све функције Log4j2.

Погледајмо различите методе помоћу којих можете додати 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 има Bill of Material (BOM) датотеку pom.xml. Ако га додате у оквиру управљања зависношћу, не морате појединачно да додајете верзије.

<!-- 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 или новију, имате опцију да увезете Log4j2 Maven Bill of Materials (BOM) како бисте одржали доследне верзије зависности. Ово се може постићи додавањем следећег у вашу датотеку 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 функционише. Испод површине, неколико компоненти чини Log4j2. Погледајмо их једну по једну.

#1. LoggerContext

LoggerContext је централна јединица система за логовање. Садржи све логере у апликацији. Такође садржи референцу на конфигурацију.

#2. Конфигурација

Конфигурација садржи све информације које захтева систем за логовање. Ово укључује логере, додатке, филтере и још много тога. У Log4j2, можете дефинисати конфигурацију користећи различите формате датотека као што су XML, JSON и YAML, а такође и програмски преко Log4j2 API-ја.

Аутоматско поновно учитавање се дешава сваки пут када се било које својство промени у конфигурацији. Дакле, не постоји потреба за поновним покретањем апликације.

#3. Logger

Главна компонента Log4j2 система је Logger. Логери се добијају унутар кода апликације помоћу наредбе LogManager.getLogger() и користе се за генерисање лог записа. Поруке лог записа могу се генерисати на различитим нивоима озбиљности, као што су отклањање грешака (debug), информације, упозорење, грешка и критична грешка.

#4. LoggerConfig

LoggerConfig је одговоран за понашање одређеног Logger-а. Дефинише понашање и подешавања за лог догађаја које генерише тај одређени логер. Омогућава конфигурацију различитих нивоа логовања, подешавање додатака и примену филтера.

#5. Филтер

Можете селективно обрадити догађаје лог записа у Log4j2 користећи филтере. Филтери се примењују на основу одређених критеријума. Ове филтере можете применити на логере или додатке. Филтери контролишу којим догађајима лог записа је дозвољено да прођу кроз систем за лог ради даље обраде. Уз помоћ филтера, понашање логовања се може детаљно подесити, осигуравајући да се обрађују само релевантни записи.

#6. Appender

Одредиште било које поруке лог записа одређује Appender. Један Logger може имати више додатака. Догађај лог записа ће бити послат свим додацима за дати Logger. Log4j2 има много унапред конфигурисаних додатака. На пример, ConsoleAppender се користи за лог поруке на конзоли, а FileAppender се користи за излаз порука у датотеку. Сваки Appender треба свој распоред који одређује како ће изгледати коначна порука лог записа.

#7. Layout

У Log4j2, Layout се користи да дефинише како ће изгледати коначна порука лог записа. Layout је повезан са Appender-ом. Док Appender одређује одредиште излаза, Layout описује како ће порука бити објављена.

5 најбољих карактеристика Log4j2

Log4j2 је богат функцијама и то је оно што га издваја од других доступних Java framework-ова за лог. Од асинхроних логера до подршке за Java 8 lambda, Log4j2 има предност у односу на друге. Погледајмо неке од значајних карактеристика овог framework-а.

#1. Проширивање функционалности помоћу додатака

У Log4j 1.x, за прављење проширења је било потребно много модификација кода. Log4j2 решава проблем проширивости увођењем Plugin система.

Можете да декларишете нови додатак користећи напомену @Plugin на својој класи. Користећи моћ додатака, можете креирати сопствене компоненте као што су филтери и додаци. Компоненте треће стране се такође могу лако додати у библиотеку.

#2. Java 8 Lambda подршка

Са издавањем Log4j2 верзије 2.4, уведена је подршка за Java 8 lambda изразе. Са lambda изразима, можете да дефинишете своју логичку логику на једном реду. Ово смањује потребу за провером у више редова или анонимним унутрашњим класама. Ово такође осигурава да се скупе методе не извршавају непотребно. На тај начин, не само да је код чистији и лакши за читање, већ се смањују и трошкови система.

Погледајмо пример где логујете резултат скупе операције, али само ако је омогућен ниво за отклањање грешака. Пре подршке за lambda, ово би се обавило коришћењем кода испод:

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

Имати више таквих случајева употребе би непотребно увело условне провере. Међутим, са Log4j2, иста радња се може извршити на следећи начин:

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

Метода expensiveOperation() се процењује само ако је омогућен ниво отклањања грешака. Нема потребе за експлицитним проверама.

#3. Асинхрони логери

Сваки догађај лог записа је улазно/излазна операција, што повећава оптерећење система. Да би ово ублажио, Log4j2 уводи асинхроне логере који раде у посебној нити од нити апликације. Када користите асинхроне логере, нит позиваоца одмах добија назад контролу након позивања методе logger.log().

Ово омогућава да се настави са логиком апликације уместо да чека да се догађај логовања заврши. Коришћењем овог асинхроног понашања постиже се већи проток логовања. Можете изабрати да сви логери буду асинхрони подразумевано, или да имате комбинацију синхроног и асинхроног понашања.

#4. Логовање без отпада

У Јави, сакупљање отпада је процес којим се некоришћени објекти у апликацији аутоматски бришу. Иако не морате ручно да водите рачуна о овој операцији, сакупљање отпада има своје трошкове.

Ако ваша апликација креира превише објеката у кратком временском периоду, процес сакупљања отпада може заузети више системских ресурса него што је потребно. Неколико библиотека за лог, укључујући претходне верзије Log4j-а, креирају много привремених објеката током процеса лог. Након тога, повећани притисак на сакупљач отпада утиче на перформансе система.

Од верзије 2.6, Log4j2 ради у режиму „без отпада“. Ово је подразумевано понашање. Због тога се објекти поново користе, а стварање привремених је знатно смањено.

Следеће слике показују како Log4j2 верзија 2.6 ублажава проблем непотребних објеката, у поређењу са Log4j2 верзијом 2.5.

У Log4j2 верзији 2.5, много привремених објеката се креира током процеса лог; Извор: apache.org

У Log4j2.6, нема привремених објеката креираних током процеса лог; Извор: apache.org

#5. Lookup-ови

У Log4j2, можете додати контекстуалне информације у своје лог записе користећи Lookup-ове. Користећи њих, можете додати податке из различитих извора, као што су својства система, променљиве окружења или прилагођене вредности. Дакле, можете укључити релевантне информације које се динамички преузимају, чинећи лог записе кориснијим.

Погледајмо пример где желите да логујете ID сесије корисника са свим линијама лог записа. Ово би вам омогућило да тражите све лог записе који одговарају ID-у сесије.

Најједноставнији начин да се то уради био би да се експлицитно дода ID сесије појединачно, што постаје тешко одржавати. Ускоро ћете можда заборавити да га додате и тако изгубити вредне информације.

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);

Бољи начин да се то уради био би коришћење Context Map Lookup-а. ID сесије се може додати у контекст нити у коду апликације. Вредност се затим може користити унутар конфигурације 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 и њихове одговарајуће вредности су наведене у наставку.

OFF0FATAL100ERROR200WARN300INFO400DEBUG500TRACE600ALLMAX ВРЕДНОСТ

Ако је ниво лог подешен на одређени ниво, онда се излазе све линије лог записа за ту одговарајућу вредност и оне изнад ње (са мањом вредношћу). Остали се игноришу.

На пример, ако поставите ниво лог на WARN, тада ће се приказати поруке WARN, ERROR и FATAL. Свака линија лог записа са другачијим нивоом биће занемарена. Ово је посебно корисно када користите исти код у различитим окружењима.

Можда ћете желети да подесите ниво лог на INFO или DEBUG када покрећете код у свом развојном окружењу. Ово ће вам омогућити да видите више лог записа и помоћи у процесу развоја. Међутим, када радите у производном окружењу, желели бисте да га поставите на ERROR. Тако ћете моћи да се фокусирате на проналажење проблема у случају да дође до било какве аномалије и нећете морати да пролазите кроз непотребне редове лог записа.

Може се десити да желите да додате сопствени прилагођени ниво лог поред унапред конфигурисаних. Log4j2 вам омогућава да то учините лако. Погледајмо како можете додати сопствене нивое лог записа и користити их у својој апликацији.

#1. Додавање прилагођеног нивоа лог помоћу конфигурационе датотеке

Можете додати прилагођене нивое лог тако што ћете их декларисати у конфигурационој датотеци.

У примеру испод, прилагођени ниво лог под називом ОБАВЕШТЕЊЕ је дефинисан са вредношћу од 450. Ово га поставља између INFO (са вредношћу 400) и DEBUG (са вредношћу од 500). То значи да ако је ниво подешен на ОБАВЕШТЕЊЕ, онда ће 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. Ова датотека садржи постојеће методе лог, заједно са новогенерисаним методама за ниво ОБАВЕШТЕЊЕ.

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 framework за лог, који нуди широк спектар функција, конфигурација, побољшања перформанси и још много тога. Пошто су лог записи веома важан део процеса развоја софтвера, поседовање робусног framework-а као што је Log4j2 побољшава способности апликације.

Флексибилност и проширивост Log4j2 омогућавају правилно снимање догађаја који се дешавају у вашој апликацији. Након тога, омогућава вам да размишљате о лог записима као о моћном алату за отклањање грешака и ревизију. Са свим својим карактеристикама и побољшањима, Log4j2 се издваја и чини се пожељним избором у разноликом спектру софтверских пројеката.

Можда ће вас занимати и ови Java IDE-ови и компајлери на мрежи.