CXF – Logging mit log4j statt commons und slf4j

Nach meinem ersten Ausflug in die CXF-Welt hab ich mir das ganze etwas näher angeschaut und mir gedacht, dass ein einheitliches Logging über das Log-Framework meiner Wahl Log4j doch eigentlich ganz nett wäre : zentrale, durchdachte Steuerung und eine sehr gute Dokumentation. Was also tun, um dieses Ziel zu erreichen? Zuerstmal gibt es in meinem Beispielprojekt zwei unterschiedliche Loggings: zum einen Commons-Logging, das per Default im CXF aktiviert ist. Und dann noch slf4j, welches sich im Jetty eingegraben hat.
Das Commons-Logging mit dem Log4j zu überschreiben ist gar nicht so schwierig und nach ein bisschen googlen bin ich zu der einfachsten Variante gekommen, es einfach per System-Property zu setzen :

in der ClientApp:

package client;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.interceptor.LoggingInInterceptor;
import org.apache.cxf.interceptor.LoggingOutInterceptor;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;

import server.IService;

public final class ClientApp {
    public void executeOperation (String text, String target) {
    	System.setProperty("org.apache.cxf.Logger", "org.apache.cxf.common.logging.Log4jLogger");
    	JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
        factory.setAddress(target);
    	// log all on incoming-chain
        factory.getInInterceptors().add(new LoggingInInterceptor()); 
        // log all on outgoing-chain
        factory.getOutInterceptors().add(new LoggingOutInterceptor());
        // get the right Interface
        factory.setServiceClass(IService.class);
        // create the Client Service
        IService service = (IService) factory.create();
        Client client = ClientProxy.getClient(service);

        HTTPConduit http = (HTTPConduit) client.getConduit();
        HTTPClientPolicy policy  = new HTTPClientPolicy();
        policy.setReceiveTimeout(1000);
        http.setClient(policy);
        service.sayHi(text);                
    }
}

und im Server :

package server;

import org.apache.cxf.jaxws.JaxWsServerFactoryBean;

public class Server {
    public void startServer(String adress) {
    	System.setProperty("org.apache.cxf.Logger", "org.apache.cxf.common.logging.Log4jLogger");
    	System.out.println("Starting Server");
    	ServiceImpl implementor = new ServiceImpl();
    	JaxWsServerFactoryBean svrFactory = new JaxWsServerFactoryBean();
    	svrFactory.setServiceClass(IService.class);
    	svrFactory.setAddress(adress);
    	svrFactory.setServiceBean(implementor);
    	// at the moment just logging on clientside
    	// svrFactory.getInInterceptors().add(new LoggingInInterceptor());
    	// svrFactory.getOutInterceptors().add(new LoggingOutInterceptor());
    	svrFactory.create();
    	System.out.println("Server ready...");
    }
}

Das slf4j mit log4j zu ersetzen habe ich leider nicht geschaft, aber es wurde von einem netten Menschen die slf4j-log4j-bridge entwickelt, mit der man ohne Probleme die slf4j-Ausgaben in das log4j-Loggingsystem übergeben kann. Diese Bridge (slf4j-log4j12) muss dann per Maven-Dependency nachgeladen werden. Und hier lauerte das nächste Problem, denn Maven lädt automatisch transitive Abhängigkeiten nach. Das ist in erster Linie kein Problem, es sei denn da beißen sich zwei Abhängigkeiten 🙁 Zum Beispiel gibt es genau in diesem Fall folgende Ausgabe beim Aufruf der Bridge :

SLF4J: Found binding in [jar:file:/D:/apache-maven-repo/org/slf4j/slf4j-jdk14/1.5.8/slf4j-jdk14-1.5.8.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/D:/apache-maven-repo/org/slf4j/slf4j-log4j12/1.5.8/slf4j-log4j12-1.5.8.jar!/org/slf4j/impl/StaticLoggerBinder.class]

Eine Exclusion in der Pom und ein neuer Eintrag bei den Dependencies für die Bridge und log4j selbst und damit kommt man hierzu :


	4.0.0
	eu.christophburmeister.maven
	firstWebservice
	0.1

	
		2.2.3
	
	
		src
		
			
				org.apache.maven.plugins
				maven-compiler-plugin
				2.3.2
				
					1.5
					1.5
				
			
		
	
	
		
			org.apache.cxf
			cxf-rt-frontend-jaxws
			2.2.3
		
		
			org.apache.cxf
			cxf-rt-transports-http
			2.2.3
		
		
			org.apache.cxf
			cxf-rt-transports-http-jetty
			2.2.3
			
				 
				
					org.slf4j
					slf4j-jdk14
				
			
		
		
			junit
			junit
			4.0
		
		
			org.slf4j
			slf4j-log4j12
			1.5.8
		
	

Anschließend muss noch die zentrale log4j.properties erstellt und abgelegt werden :

#
# our log4j properties / configuration file
#
# Set root logger level to DEBUG and its only appender to CONSOLE_APPENDER.
log4j.rootLogger=INFO, CONSOLE_APPENDER

# CONSOLE_APPENDER is set to be a ConsoleAppender.
log4j.appender.CONSOLE_APPENDER=org.apache.log4j.ConsoleAppender

# CONSOLE_APPENDER uses PatternLayout.
log4j.appender.CONSOLE_APPENDER.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE_APPENDER.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Und folgend noch die komplette Verzeichnis-Struktur :

Damit läuft das gesamte Logging jetzt über log4j. Toll 😀