Home

De Franciscis Dimitri

Homepage & Laboratorio Creativo

Primary links

  • Homepage
  • Curriculum Vitae
  • Servizi
  • Fotografia
  • Libri e guide
  • Contattami
Home Blog dimitri's blog

Configurare path relativi per Log4j utilizzando Spring

Submitted by dimitri on Mer, 26/11/2008 - 13:14
  • Informatica
  • j2ee
  • java
  • jee
  • log4j
  • servlet
  • spring
  • tomcat
  • web application
  • web.xml

Uno dei problemi più fastidiosi quando si esegue il deploy di una web application Java è la corretta configurazione del logging. In questo articolo vedremo come Spring ci può venire in aiuto.

Path assoluti vs path relativi

Il problema principale dei path assoluti è che vanno  modificati per ogni installazione dell'applicazione, richiedendo tempo e attenzione. Proviamo allora a configurare la web application per utilizzare path relativi.

Qui sotto vediamo un primo tentativo di configurazione che scrive su file (salvare il file come /WEB-INF/classes/log4j.properties):

log4j.rootLogger=ERROR, stdout, rollingFile

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - <%m>%n

log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.File=WEB-INF/logs/application.log
log4j.appender.rollingFile.MaxFileSize=512KB
log4j.appender.rollingFile.MaxBackupIndex=10
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.rollingFile.Encoding=UTF-8

Il file scelto per i log è WEB-INF/logs/application.log, ma se proviamo questa soluzione (basta installare l'applicazione, scrivere del codice che invoca i metodi di Log4j e far partire Tomcat) ci rendiamo conto che i path relativi non hanno funzionano come vorremmo! I file vengono scritti infatti in TOMCAT_HOME/bin/WEB-INF/logs; questo in molti casi non è accettabile, per motivi di sicurezza e stabilità.

Non solo, se scriviamo questo (nel file log4j.properties):

log4j.appender.rollingFile.File=/WEB-INF/logs/application.log

(notare lo slash "/" prima di "WEB-INF"), addirittura tenterà di scrivere in C:\WEB-INF\logs (Windows) o in /WEB-INF/logs (Linux/Unix). A quanto pare quindi non c'è un modo facile e soprattuto standard (cioè senza dover ricorrere a codice o librerie) per indicare un path relativo per Log4j. Bisogna usare un path assoluto.

Utilizzare i path assoluti però è una strada in salita: dovremmo personalizzare i nostri pacchetti per ogni installazione, rubando tempo prezioso allo sviluppo. Come fare allora?

Spring ci viene in aiuto

Come al solito gli ingegneri di Spring hanno pensato a tutto, anche a questa eventualità, inventando un modo veramente poco invasivo e allo stesso tempo flessibile per superare questa difficoltà: il suo nome è Log4jWebConfigurer.

Questa classe si occupa della configurazione appunto di Log4j ed è stata pensata proprio per superare le difficoltà che abbiamo visto sopra. Il suo funzionamento è semplice: ai file di configurazione viene messa a disposizione una nuova variabile ${webapp.root}, che contiene il percorso assoluto della web application nel filesystem. La nostra configurazione quindi diventera:

log4j.appender.rollingFile.File=${webapp.root}/WEB-INF/logs/application.log

Configurare Spring

Per prima cosa, se non l'abbiamo già fatto, aggiungiamo spring.jar al classpath, copiandolo in /WEB-INF/lib.

Apriamo ora il file /WEB-INF/web.xml, e aggiungiamo queste righe prima delle definizioni di <servlet>:

<listener>
  <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

Purtroppo questa soluzione non basta, perché rimane un problema: nella root del filesystem viene creata una directory "${webapp.root}" (esattamente come è scritta)!! Ma allora siamo al punto di partenza, direte voi...

Il problema è che, molto discutibilmente, Log4j prende l'iniziativa: si accorge che nel classpath (in /WEB-INF/classes appunto) c'è un file che si chiama log4j.properties e lo utilizza per auto-configurarsi. Tutto ciò avviene in maniera autonoma, quindi senza aspettare che venga definita la famosa variabile ${webapp.root}, da qui il problema del percorso. Se non ci avete capito nulla, non vi preoccupate: io stesso facevo fatica a crederlo una volta scoperto!

Per fortuna però gli autori di Spring hanno pensato anche a questo, introducendo il context-param log4jConfigLocation, vediamo come usarlo.

Innanzi tutto bisogna assolutamente cambiare nome al file log4j.properties, altrimenti Log4j tenterà di leggerlo da sè, combinando quindi i guai che abbiamo visto sopra. Potremmo chiamarlo ad esempio log4j-myapp.properties.

Poi bisogna aggiungere queste righe a web.xml:

<context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>/WEB-INF/classes/log4j-myapp.properties</param-value>
</context-param>

Et voilà! Log4j piegato ai nostri voleri ;)

Non finisce qui...

Cosa succederebbe se ci fossero più web application che usano questo meccanismo? Molto semplice: una gran confusione! Il fatto è che la variabile ${webapp.root} viene definita come system property; la specifica delle Servlet non è molto chiara a tal punto, in linea teorica dovrebbe essere possibile isolare le system properties per web application, ma purtroppo in Tomcat non è così.

Non vorrei sembrare ripetitivo, ma mi tocca: anche in questo caso, ancora una volta, Spring ha pensato ad una soluzione...

Possiamo definire un nuovo context-param:

<context-param>
  <param-name>webAppRootKey</param-name>
  <param-value>root-mia-istanza</param-value>
</context-param>

Questo parametro specifica il nome della variabile di sistema che indica la root. Dovremmo quindi mettere mano (l'ultima volta!) al nostro file di configurazione:

log4j.appender.rollingFile.File=${root-mia-istanza}/WEB-INF/logs/application.log

L'unico accorgimento a questo punto è di utilizzare un nome differente per ogni istanza di web application, per evitare conflitti.

Conclusioni

Come vedete i problemi ci sono, ma ci sono anche le soluzioni. Sia Log4j che Spring hanno dei difetti, ma i pregi li superano di gran lunga, per cui bisogna fare buon viso a cattivo gioco e non darsi per vinti accontentandosi di soluzioni parziali o poco pratiche.

Linkografia

  • Spring framework
  • Log4j
  • Tomcat

  • dimitri's blog
  • Aggiungi un commento
  • 4678 letture
  • English

Ottimo, mi hai risolto n-esimo problema

Submitted by Anonymous on Mar, 03/03/2009 - 11:38.
Molto utile. Grazie. Nella mia ignoranza non ho capito subito l'ultima parte 'Non finisce qui' e mi permetto, per altri che abbiano il mio stesso problema, di aggiungere le configurazione dei miei files esattamente come le ho scritte. web.xml webAppRootKey rootTryApp log4j-myapp.properties log4j.appender.R.File=${rootTryApp}/WEB-INF/log4j/application.log Enjoy, it will run. Theowl
  • rispondi

multi-istanza

Submitted by dimitri on Mar, 17/03/2009 - 13:39.
L'ultima parte serve per quando si ha bisogno di installare più di una web application: infatti spring definisce la variabile ${webapp.root} come system property, visibile cioè a *tutte* (!) le web application. In pratica ogni webapp caricata sovrascrive questa variabile, e ciò è ovviamente un problema. Utilizzare un nome univoco per ogni webapp assicura che le variabili non vengano sovrascritte.
  • rispondi

Fantastico

Submitted by Anonymous on Mar, 07/07/2009 - 16:25.
Grazie Dimitri risolto problema Filippo
  • rispondi

Guerra atomica di framework per scrivere un log!

Submitted by Anonymous on Mar, 22/09/2009 - 10:44.
Non vorrei essere troppo lapidario, ma forse dovevi indagare prima come avviene la scrittura dei log del tuo servlet container o application server. Quando viene avviato il server, nella riga di comando ci sono sempre un po' di variabili di ambiente che servono a qualcosa :D Nel caso di tomcat: log4j.appender.rollingFile.File=${catalina.home}/logs/tuaapp.log E ancora: Che intendi quando dici occorre rinominare il file di log4j? Mai sentito parlare del Log4jConfigListener? Stai facendo un po' di confusione! Ps: uso Spring da anni ma ti assicuro che non serve fare tutto questo casino per impostare un appender per Log4J!
  • rispondi

 > Non vorrei essere troppo

Submitted by dimitri on Mar, 22/09/2009 - 11:20.

 > Non vorrei essere troppo lapidario, ma forse dovevi indagare prima come
> avviene la scrittura dei log del tuo servlet container o application server.
> Quando viene avviato il server, nella riga di comando ci sono sempre un po' di
> variabili di ambiente che servono a qualcosa :D Nel caso di tomcat:
> log4j.appender.rollingFile.File=${catalina.home}/logs/tuaapp.log 

Uno degli scopi dell'articolo è mostrare come ottenere una web application che utilizzi *in automatico* dei path relativi per salvare i file di log. Nel primo paragrafo parlo appunto di come *durante lo sviluppo* sia scomodo dover configurare ogni volta dei path assoluti. E non pensare che sia un'attività rara: pensa a quando si fa un branch, quando si deve fare un bugfix su una vecchia applicazione, eccetera.

E non scendo nel merito di quanto sia deprecabile configurare una web application da... riga di comando!?!

> E ancora: Che
> intendi quando dici occorre rinominare il file di log4j? Mai sentito parlare
> del Log4jConfigListener? Stai facendo un po' di confusione!  

Intendo quello che c'è scritto: se non rinomini log4j.properties in qualcos'altro, Log4J se lo legge *prima* di Log4jConfigListener, ignorando l'eventuale variabile ${webapp.root}

> Ps: uso Spring da
> anni ma ti assicuro che non serve fare tutto questo casino per impostare un
> appender per Log4J!

Per impostare un appender che scriva in un path *relativo alla webapp* però sì. Se conosci qualche altro metodo ti prego di scrivermi in privato così se vuoi posso pubblicare il tuo articolo a fianco di questo, che ne dici?

Ciao e grazie ;)

  • rispondi

Ecco lo sapevo .. te la sei

Submitted by Anonymous on Mar, 22/09/2009 - 11:54.
Ecco lo sapevo .. te la sei presa! Il tuo sforzo è encomiabile ma in questo modo rischi di suggerire soluzioni che apparentemente funzionano ma non sono delle best practices! Qui di seguito il codice per configurare correttamente il config listener: [CODE] log4jConfigLocation /WEB-INF/log4j.properties org.springframework.web.util.Log4jConfigListener [/CODE] in questo modo non devi rinominare il file di log4j. Secondo: nel caso di Tomcat è necessario specificare la webapp root key per ogni web app che utilizzi il Log4jConfigListener e a quel punto magari sei anche libero di utilizzare la variabile nel file di configurazione di log4j. Ma se non stai utilizzando tomcat, puoi continuare ad utilizzare perfettamente la variabile di default webapp.root o, come ho fatto io per i miei scopi, la variabile catalina.base Infine: prova ad utilizzare Maven o Ant per effettuare il deploy delle tue applicazioni .. utilizzando dei placeholder avrai libertà di configurazione in maniera "automatica" come dici tu .. ps: ma dove hai letto che configuro le applicazioni da riga di comando? :D Ciao pischello :D
  • rispondi

Ma no :)

Submitted by dimitri on Mar, 22/09/2009 - 14:16.

Valà, prendersela per delle critiche costruttive? Non sia mai...

Guarda, ti do ragione sul fatto che l'ultimo punto ("Non finisce qui...") effettivamente è una forzatura; il mio scopo era illustrare come ottenere i log su path relativi una volta per tutte all'inizio dello sviluppo (al checkout, all'installazione, come ti pare) e l'ultima soluzione (rinominare la variabile) va un po' contro questo principio.

Riguardo alla linea di comando... beh scusa l'hai scritto tu, che ci posso fare???

Piuttosto, mandami una mail in privato invece di postare anonimamente, mi interessa conoscerti per scambiare quattro chiacchiere. Inoltre, dopo questi tuoi interventi, mi sono convinto ad aggiornare l'articolo con queste nuove "scoperte" e ho bisogno di qualcuno che mi faccia una revisione senza paura di fare osservazioni puntigliose ;)

Dimitri

  • rispondi

Riga di comando!

Submitted by Anonymous on Mar, 22/09/2009 - 14:32.
Ah! La riga di comando! Intendevo dire: quando viene avviato l'application server ( ad es. Tomcat ) tra i parametri di invocazione ci sono delle variabili d'ambiente che ovviamente possono essere utilizzate nella JVM. Allora, a mio avviso, dovevi procedere investigando un po' di più su come l'application server scrive i suoi logs. A questo punto avresti scoperto l'esistenza delle variabili di cui sopra. Dalla mia alta esperienza ti posso confermare che solitamente i logs vanno "per default" sotto la cartella di logs insieme a tutti gli altri. Perché? Perché i sistemisti conoscono solo quella cartella! :D Quindi tanto vale utilizzare ( nel caso di tomcat ) catalina.home . Tutto qui. Mando una mail più tardi che oggi mi hai fatto perdere fin troppo tempo! ;-)
  • rispondi

FeedFlare

Navigazione

  • Contenuti recenti
  • Cerca
  • Tags
  • Articoli più letti

De Franciscis Dimitri feeds

  • De Franciscis Dimitri - Homepage &amp; Laboratorio Creativo

Secondary links

  • Note legali
  • Pubblicità su www.megadix.it

Copyright 2007 De Franciscis Dimitri - p.iva 05327790969

website monitoring service

Web4Web

RoopleTheme