SLF4J a Spring MVC a Jetty a Maven

Ako naštartovať logovanie v springáckej MVC aplikácii, ktorá sa testuje na Jetty kontajneri spúšťanom v Mavene?

Je taká smutná anektoda: do .BARu vojde commons-logging, java.util.logging, slf4j a logback, ale chvíľke šaškovania so závislosťami sa to dá rozbehať.

Bežná logovacia aplikácia

Kto chce byť in, loguje cez slf4, teda logovacie API, na ktoré možno zavesiť ľubovoľnú vhodnú implementáciu (obvykle logback).

Nahoďme teda závislosť:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.0.13</version>
    <scope>runtime</scope>
</dependency>

Artefakt logback-classic si donesie tranzitívnu závislosť na logback-core a… závislosť na slf4j-api. Špecialitou je jedine scope nastavený na runtime: naša aplikácia síce závisí na logbacku, ale nepotrebuje ho na úspešné skompilovanie. Potrebuje ho len v čase behu (dokonca len ako voliteľnú súčasť, ale to v Mavene nastaviť rozumne nevieme).

Teraz už môžeme hromadiť a hromadiť záznamy v logovacích súboroch.

Rúra milióna frameworkov

Ak dotiahneme do projektu Spring, dostaneme úplne zadarmo commons-logging (a.k.a. druhý najväčší omyl v logovacej histórii Javy). To však znamená, že budeme mať dve sady odlišných logovacích API a nádherný chaos:

  • naša aplikácia bude logovať do SLF4J cez logback.
  • a Spring bude logovať cez commons-logging, ktorý, nenájduc žiadnu implementáciu, naposiela všetko cez štandardný java.util.logging (prvý najväčší omyl v dejinách Javy).

Autor SLF4J (a zároveň pôvodca log4j a zároveň autor logbacku) šípil, že takýto guláš v projektoch nastane a zaviedol sadu adaptérových modulov, kde môžete napr. zobrať commons-logging a napojiť ho na slf4j, ktorý napojíte na logback. Tým môžete napríklad presvedčiť Spring, aby používal rovnaké logovacie mechanizmy ako vaša aplikácia.

Logovací event teda prelezie radostnou rúrou frameworkov:

|--------|    |-----------------|    |----------------|    |-----------|    |---------|
| Spring |--->| commons-logging |--->| jcl-over-slf4j |--->| slf4j-api |--->| logback |
|--------|    |-----------------|    |----------------|    |-----------|    |---------|

Zo Springu teda vyhoďme commons-logging a miesto neho dajme adaptérový modul jcl-over-slf4j (Jakarta Commons Logging presmerovaný na SLF4).

Závislosť bude:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>jcl-over-slf4j</artifactId>
    <version>1.7.5</version>
</dependency>

To je jeden krok, potrebujeme ešte vyhodiť závislosť na commons-logging zo Springu. V Mavene sa to rieši cez dependency exclusions, čiže zakazovanie závislostí:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>3.2.3.RELEASE</version>
    <exclusions>
        <exclusion>
            <artifactId>commons-logging</artifactId>
            <groupId>commons-logging</groupId>
        </exclusion>
    </exclusions>
</dependency>

Štandardné šialené nastavenie

Ak by sme dotvorili webový projekt (napr. podľa dema na GitHube) a spustili ho cez mavenovský plug-in:

mvn jetty:run

uvideli by sme záplavu DEBUG hlášok (a nekonečný štart kontajnera Jetty). Už len jedna návšteva kontroléra cez prehliadač by spôsobila 9 hlásení z útrob Spring MVC:

17:15:59.578 [qtp209879718-31] DEBUG o.s.web.servlet.DispatcherServlet - DispatcherServlet with name 'dispatcher' processing GET request for [/log]
17:15:59.586 [qtp209879718-31] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Looking up handler method for path /log
17:15:59.594 [qtp209879718-31] DEBUG o.s.w.s.m.m.a.RequestMappingHandlerMapping - Returning handler method [public java.lang.String sk.upjs.ics.novotnyr.mjss.LoggingController.onLog()]
17:15:59.595 [qtp209879718-31] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Returning cached instance of singleton bean 'loggingController'
17:15:59.598 [qtp209879718-31] DEBUG o.s.web.servlet.DispatcherServlet - Last-Modified value for [/log] is: -1
17:15:59.624 [qtp209879718-31] INFO  s.u.i.n.mjss.LoggingController - Accessing onLog()
17:15:59.664 [qtp209879718-31] DEBUG o.s.w.s.m.m.a.RequestResponseBodyMethodProcessor - Written [OK] as "text/html" using [org.springframework.http.converter.StringHttpMessageConverter@9efb34]
17:15:59.665 [qtp209879718-31] DEBUG o.s.web.servlet.DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
17:15:59.667 [qtp209879718-31] DEBUG o.s.web.servlet.DispatcherServlet - Successfully completed request

To je síce chvályhodné, lebo vidíme, čo sa deje vo vnútri, ale nie vždy je to to najdôležitejšie.

Vlastný konfigurák pre logback

Ak dodáme do src/main/resources konfigurák logback.xml, môžeme obmedziť úroveň logovacích hlášok:

<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="sk.upjs.ics.novotnyr.mjss" level="DEBUG" />
</configuration>

Všetky loggery budú logovať len hlášky INFO a dôležitejšie, ale špeciálne náš kontrolér bude logovať na úrovni DEBUG i vyššej. Týmto dramaticky obsekáme logovacie hlášky z Jetty

Ako vyzerá zabalený WAR?

Pozrime sa ešte na zabalený WAR:

mvn package

Výsledný WAR bude vo WEB-INF/lib obsahovať:

jcl-over-slf4j-1.7.5.jar
logback-classic-1.0.13.jar
logback-core-1.0.13.jar
slf4j-api-1.7.5.jar

spring-aop-3.2.3.RELEASE.jar
spring-beans-3.2.3.RELEASE.jar
spring-context-3.2.3.RELEASE.jar
spring-core-3.2.3.RELEASE.jar
spring-expression-3.2.3.RELEASE.jar
spring-web-3.2.3.RELEASE.jar
spring-webmvc-3.2.3.RELEASE.jar

aopalliance-1.0.jar

Všimnime si, že sa v ňom objavia všetky potrebné logovacie knižnice. Konfigurák logback.xml bude vo WEB-INF/classes.

Sumár

Logovanie v konglomeráte knižníc Spring/SLF4J/Logback/Maven/Jetty je nakoniec jednoduché:

  • nahodíme závislosť na logbacku
  • nahodíme závislosť na jcl-over-slf4j
  • zakážeme závislosť na commons-logging
  • nezabudneme na logback.xml,

a ideme.

Odkazy

2 thoughts on “SLF4J a Spring MVC a Jetty a Maven

  1. No jo, velka veda, ktorej zlozitost narasta s poctom pouzitych frameworkov. Ja excludujem commons-logging so springu. Na logovanie Logger z log4j + xml/props file a deps (groupId -> artifactid -> version): log41 -> log4j -> 1.2.17 org.slf4j -> slf4j-api -> 1.6.4 org.slf4j -> jcl-over-slf4j -> -||- org.slf4j -> slf4j-log4j12 -> -||- org.slf4j -> jul-to-slf4j -> -||-

    Ale pyta si to zmenu, mozno zaclenime logback.

    Jaro.

  2. Na starých projektoch je to zrejme jednoduchšie: stačí commons-logging => log4j a je to :-)

    Akurát mnoho OSS projektov, čo môžu, migrujú na SLF4j, lebo commons-logging s tým dynamickým loadovaním je nešťastie. (Okrem Springu, lebo spätná kompatibilita, viď [1] a SPR-5327)

    Ešte fascinujúce je, že ktosi vykopal log4j a urobil verziu 2.0, akurát netuším, či niečo spravili s kritickými chybami 1.x, alebo len zlepšili performance.

    My na nových projektoch automaticky nahadzujeme SLF4J/logback, ostatné možnosti IMHO nemajú nejaký valný zmysel.

    [1] http://blog.springsource.org/2009/12/04/logging-dependencies-in-spring/

Pridaj komentár

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

Môžete použiť tieto HTML značky a atribúty: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">