Logging tramite LOG4J e SysLog

Uno dei framework per il logging più popolare disponibile in ambiente Java è sicuramente LOG4J. Si ha a volte l'esigenza di fare confluire i log generati da un programma Java in quelli di sistema; in ambiente Linux i log sono gestiti dal demone Syslog (o qualche sua variante).

LOG4J ci mette a disposizione un "appender"  che si occupa di trasferire i messaggi di log al syslog. Tale appender è il SyslogAppender; vediamo un esempio di come sia possibile utilizzarlo.

 

 

--- inizio file HelloWorld.java ---

 

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class HelloWorld {

public static void main(String[] args) {

PropertyConfigurator.configure("log4j.properties");
Logger logger = Logger.getLogger(HelloWorld.class);
logger.info("Hello world");

}

}

--- fine file HelloWorld.java

Per riuscire a redirigere il messaggio di log "Hello world" si deve utilizzare il seguente file di configurazione di log4j:

--- inizio file log4j.properties ---
log4j.rootLogger=INFO, SYSLOG
log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender
log4j.appender.SYSLOG.syslogHost=127.0.0.1
log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout
log4j.appender.SYSLOG.facility=DAEMON
--- fine file log4j.properties ---
 
Nota: ho effettuato le prove su una Ubuntu 10.10 che come demone utilizza RSYSLOG; per riuscire a far ricevere a rsyslog i messaggi di log4j è stato necessario aggiungere le seguenti righe al file /etc/rsyslog.conf (dopo aver effettuato le modifiche è necessario riavviare il servizio con 'restart rsyslog'):
$ModLoad imudp
$UDPServerRun 514
 
La comunicazione tra Log4j e Syslog avviene dunque attraverso messaggi UDP verso localhost. Lavorando in locale potrebbe essere preferibile non ricorre alla comunicazione via rete, ma accedere direttamente alla API C per l'utilizzo di syslog. Adesso vedremo un possibile approccio che ricorre a JNA.
 
Le API C (visualizzabili tramite il comando  'man syslog') sono:
 
       void openlog(const char *ident, int option, int facility);
       void syslog(int priority, const char *format, ...);
       void closelog(void);
 
Ecco l'interfaccia Java/JNA necessaria per il loro utilizzo:

 

 
--- inizio file C.java ---
public interface C extends Library {
        C LIB = (C) Native.loadLibrary("c", C.class);    
        public void openlog(String ident, int option, int facility);
        public void syslog(int priority, String format, String ...args);
        public void closelog();
}

 

--- fine file C.java ---
 
A questo punto possiamo scrivere un nostro appender che utlizza queste API (attenzione è solo un proof of concept con diverse limitazioni: utilizza la facilty fissa DAEMON ed un livello di logging fisso ad INFO)
 
--- inizio file SyslogJnaAppender.java ---
 

 

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;
 
import com.sun.jna.Library;
import com.sun.jna.Native;
 
public class SyslogJnaAppender extends AppenderSkeleton {
 
private static final int LOG_DAEMON = 24;
private static final int LOG_NDELAY = 8;
private static final int LOG_INFO = 6;
 
private String ident = "";
private boolean opened = false;
 
@Override
public boolean requiresLayout() {
return false;
}
 
@Override
protected void append(LoggingEvent event) {
if(!opened) {
C.LIB.openlog(ident, LOG_NDELAY, LOG_DAEMON);
opened = true;
}
C.LIB.syslog(LOG_INFO, "%s", event.getMessage().toString());
}
 
@Override
public void close() {
if(opened) {
C.LIB.closelog();
}
}
 
public void setIdent(String ident) {
this.ident = ident;
}
 
}
 

 

--- fine file SyslogJnaAppender.java ---
 
Vediamo come va invece modificato il file log4j.properties per utilizzare il nostro appender jna:
log4j.rootLogger=INFO, SYSLOG
log4j.appender.SYSLOG=SyslogJnaAppender
log4j.appender.SYSLOG.ident=prova
 
Riassumendo, abbiamo quindi visto come configurare LOG4J per comunicare con i sistema di logging di ogni distribuzione Linux, cioè con syslog. La comunicazione LOG4J/SYSLOG può avvenire tramite protocollo UDP (funzionalità di default già integrata in log4j) oppure tramite chiamate dirette a librerie C (scrivendo un appender ad hoc che si interfaccia alla libc tramite JNA).