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();
}
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).