SLF4J a Spring MVC a Jetty a Maven so spoločným logback.xml

Embedded Jetty v Mavene je skvelý príklad mäteže logov. Ako presvedčiť server, aby logoval rovnakým spôsobom ako vaša aplikácia, ak používate Spring? Toto je pokračovanie článku SLF4j a Spring MVC a Jetty a Maven.

Aktualizácia 6. 7. 2014: Postup funguje len v Mavene 3.0.x. Novšie verzie Mavenu využívajú na interné logovanie slf4j, čo situáciu komplikuje a tento postup nefunguje. V takom prípade využije conf\logging\simplelogger.properties v inštalačnom adresári Mavenu na doladenie konfigurácie. Vysvetlenie sa nachádza v dokumentácii k Mavenu.

Najprv ukážka z konzoly:

2013-08-07 17:40:18.047:INFO:oejs.ServerConnector:main: Started ServerConnector@a5c7c18{HTTP/1.1}{0.0.0.0:8080}
[INFO] Started Jetty Server
[INFO] Console reloading is ENABLED. Hit ENTER on the console to restart the context.
17:40:45.414 [qtp1433716617-27] DEBUG s.u.i.n.mjss.LoggingController - Accessing onLog()

Tieto štyri riadky zo života ukazujú tri (!) výstupy z rozličných systémov:

  • prvý je logovaný samotným Jetty
  • dvojica ukazuje log z Mavenu
  • a posledný riadok vznikol prúdením Spring -> commons-logging -> SLF4J -> logback

Jetty môžeme veľmi jednoducho prehodiť na používanie SLF4J: pretože už od dávna (minimálne od verzie 8) môže interne logovať cez túto knižnicu. Možnosť výberu je pomerne zložitá, ale nám stačí vedieť, že ak sa v CLASSPATH servera objaví SLF4J, Jetty ju použije.

Budeme potrebovať:

  • zaviesť závislosti do pom.xml
  • nastaviť cestu ku konfiguračnému súboru logback.xml

POM.XML

V Mavene to vyriešime dodaním závislostí pre plug-in Maven Jetty.

<plugin>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-maven-plugin</artifactId>
    <version>9.0.4.v20130625</version>
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.13</version>
        </dependency>
    </dependencies>             
</plugin>

Pre plug-in (pozor, nie pre projekt!) sme dodali požadovaný SLF4J a rovno aj logback.

Cesta k logback.xml

Ak by sme logovali v normálnej webovej aplikácii, Logback konfigurujeme cez logback.xml, ktorý sa má ocitnúť v CLASSPATH. Tradične to znamená, že ho hodíme do koreňového balíčka, čo v zmysle mavenovských konvencií do src/main/resources/logback.xml. Vo výslednej webovej aplikácii sa tento súbor ocitne zrejme vo WEB-INF/classes.

Jetty, čo je embeddnutá v Mavene, však tieto finty nemôže použiť (a nefungujú ani bežné postupy pre samostatne bežiacu Jetty mimo Mavenu). Prakticky existujú dva spôsoby, ako uviesť cestu k logback.xml:

  • nastavením system property z príkazového riadku,
  • uvedením do pom.xml.

Ukážeme si spôsob, kde v projekte aj Jetty, aj naša aplikácia bude používať rovnaký súbor logback.xml. Konfigurácia logovania sa teda scentralizuje na jednom mieste.

Nastavenie systémového parametra

Ak nastavíme systémový parameter (system property) logback.configurationFile, Jetty ho použije na nájdenie súboru. Hodnota musí obsahovať absolútnu cestu k súboru, napr. takto:

mvn -Dlogback.configurationFile=C:\Projects\maven-jetty-spring-slf4j-demo\src\main\resources\logback.xml jetty:run

Nechcel by som to písať z príkazového riadku…

Uvedenie do pom.xml

Druhou možnosťou je zavedenie systémového parametra priamo do pom.xml. Plug-in pre Jetty síce podporuje v konfigurácii parameter systemProperties, ale v tejto situácii to nefunguje:

<plugin>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-maven-plugin</artifactId>
  <configuration>
    <systemProperties>
      <systemProperty>
        <name>fooprop</name>
        <value>222</value>
      </systemProperty>
    </systemProperties>

Dokumentácia Jetty sa snaží hmlisto a nie celkom úspešne vysvetliť, že prečo to tak je, ale aspoň ponúka riešenie: cez ďalší plugin org.codehaus.mojo:properties-maven-plugin:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>properties-maven-plugin</artifactId>
    <version>1.0-alpha-2</version>
    <executions>
        <execution>
            <phase>validate</phase>
            <goals>
                <goal>set-system-properties</goal>
            </goals>
            <configuration>                         
                <properties>
                    <property>
                        <name>logback.configurationFile</name>
                        <value>${basedir}/src/main/resources/logback.xml</value>
                    </property>
                </properties>
            </configuration>
        </execution>
    </executions>
</plugin>   

Z plug-inu využijeme goal set-system-properties, ktorý naviažeme na fázu validate: je to jedna z úvodných fáz životného cyklu mavenovského zostavovania projektu a teda sa vykoná skôr než spustenie Jetty.

Príslušný parameter musíme uviesť s celou cestou: ale v Mavene máme zabudovanú premennú basedir, ktorá jednak ušetrí písanie a jednak je robustná krížom cez rozličné adresárové štruktúry.

Nastavenie logback.xml

Zmienený logback.xml je potom jednoduchý:

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>

    <logger name="org.springframework" level="DEBUG" />
    <logger name="org.eclipse.jetty.plus.webapp.PlusConfiguration" level="WARN" />
    <logger name="sk.upjs.ics.novotnyr.mjss" level="DEBUG" />
</configuration>

Implicitná úroveň logovania je na úrovni INFO, triedy zo Springu sme nastavili na DEBUG, špecifickú triedu z Jetty na úroveň WARN a triedy z nášho projektu budú opäť DEBUG.

Ukážkový log potom vyzerá nasledovne:

11:34:02.721 [main] INFO  org.eclipse.jetty.server.Server - jetty-9.0.4.v20130625
11:34:04.304 [main] INFO  / - Spring WebApplicationInitializers detected on classpath: [sk.upjs.ics.novotnyr.mjss.WebInitializer@10a488e4]
11:34:04.307 [main] DEBUG s.u.ics.novotnyr.mjss.WebInitializer - No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context

Prvá hláška patrí Jetty, druhá Springu a tretia je z útrob nášho projektu.

Odkazy

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *