Nerd Notes

/dev/brain: no space left on device

Posts Tagged ‘commons logging

Ulteriori elucubrazioni sull’impiego di slf4j e logback

with one comment

In seguito al mio precedente post scrivi messaggi di log decenti, altrimenti Gesù piange, alcuni colleghi hanno iniziato ad usare slf4j e logback come framework di logging. Quando si inizia ad usare questa roba ci si scontra subito con un problema classico che è quello della convivenza fra framework di logging. Senza scendere troppo in dettagli immediatamente, dirò che lo scopo di questo post è quello di spiegare come fare in modo che, una volta consolidata la scelta di slf4j come api e logback come implementazione, indipendentemente dai framework di logging in uso nella nostra applicazione, l’output dei log sia gestito in maniera centralizzata ed unica tramite logback (quindi tramite il file logback.xml).

In altre parole, nella nostra applicazione ci troveremo a dover gestire una situazione in cui ci saranno classi nostre che usano l’api di slf4j ed altre classi non gestite da noi che usano log4j piuttosto che jakarta-commons-logging (jcl per gli amici) o java.util.logging (jul per gli amici).

Prima di tutto una premessa doverosa: le istruzioni che seguono funzioneranno solo in un ambiente in cui il classloader dà la precedenza nel caricamento delle classi a quelle che si trovano all’interno dei nostri artefatti e non a quelle fornite con l’application server. Per capirci, se il nostro applicativo è un war da installare su tomcat, funzionerà tutto correttamente. Se invece il nostro applicativo è una ear da installare su jboss application server, le indicazioni non funzioneranno perché il classloader di jboss darà la precedenza alle classi di log4j e jakarta-commons-logging fornite col server.

In soldoni, bisogna fare in modo che nella directory WEB-INF/lib del nostro war non ci siano i jar delle implementazioni di log4j e jakarta-commons-logging ma piuttosto dei jar di slf4j che contengono delle classi wrapper che si sostituiscono a quelle di log4j e jakarta-commons-logging facendo in modo che l’implementazione delle interfacce di log venga intercettata e dirottata verso slf4j e quindi logback.

Questo in maven si traduce in un pom di questo tipo:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging</artifactId>
                <version>1.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>commons-logging</groupId>
                <artifactId>commons-logging-api</artifactId>
                <version>1.1</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.15</version>
                <scope>provided</scope>
            </dependency>

            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>1.5.10</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.5.10</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>log4j-over-slf4j</artifactId>
                <version>1.5.10</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>0.9.18</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>0.9.18</version>
            </dependency>
        </dependencies>
</dependencyManagement>

Bene. Ora, a cosa serve un blocco dependencyManagement? A dichiarare dei default, tant’è vero che tipicamente la sezione dependencyManagement viene specificata nel pom padre del progetto, quello principale per capirci. Andiamo un po’ a vedere i vari blocchi a cosa servono.

Le prime tre dichiarazioni servono per dichiarare in modo globale che gli artefatti (e quindi i relativi jar) di commons-logging, commons-logging-api e log4j sono forniti dal contesto di deployment (in altre parole sono forniti con il jdk oppure si trovano in una directory lib o endorsed dell’application server). In pratica dichiarare come provided queste dipendenze è  un trucco per fare in modo che i jar di log4j e di commons-logging non vadano a finire dentro WEB-INF/lib, altrimenti bisognerebbe analizzare l’albero delle dipendenze del progetto e dichiarare tante di quelle esclusioni sulle dipendenze transitive da morirci gonfi.

Le altre dichiarazioni servono solo a stabilire dei default sulle versioni dei jar che ci interessano. Le dichiarazioni vere e proprie delle dipendenze va fatta sul pom che produce il war, a questo punto senza specificare versioni perché verranno impiegati i default stabiliti in precedenza dentro al blocco dependencyManagement:

        <dependencies>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>log4j-over-slf4j</artifactId>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
            </dependency>
        </dependencies>

La verifica ultima va fatta andando a controllare il contenuto del war. Se abbiamo fatto bene il nostro lavoro, nella directory WEB-INF/lib del war non dobbiamo trovare alcun jar di commons-logging né di log4j. Dovremmo invece trovare questi jar:

  • jcl-over-slf4j-1.5.10.jar
  • log4j-over-slf4j-1.5.10.jar
  • logback-classic-0.9.18.jar
  • logback-core-0.9.18.jar
  • slf4j-api-1.5.10.jar

Per ulteriori dettagli vi rimando alla documentazione dei relativi framework, in particolare alla pagina bridging legacy APIs nella documentazione di slf4j.

Advertisements

Written by Mirko Caserta

October 13, 2010 at 3:10 pm