Logging with SLF4J and Guice

After getting angry at the java.util.logger once again i was thinking how to replace it with the SLF4J logger. Although Guice provides a very nice internal binding to java.util.logger, slf4j does offer a much nicer syntax.
The devil is in the detail, as allways … if you want your logger to be initialized with the current class you can’t simply inject the logger … But .. there is a nice tutorial on the guice wiki about injecting a log4j logger. SLF4J works the same.

First you need a new annotiation, like InjectLogger:

import static java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({FIELD}) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface InjectLogger {
}

next is a TypeListener, that listenes to org.slf4j.Logger classes with the annotiation InjectLogger:

import java.lang.reflect.Field;

import org.slf4j.Logger;

import com.google.inject.TypeLiteral;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;

public class Slf4jTypeListener implements TypeListener {

	public <I> void hear(TypeLiteral<I> aTypeLiteral, TypeEncounter<I> aTypeEncounter) {
		
		for (Field field : aTypeLiteral.getRawType().getDeclaredFields()) {
			if (field.getType() == Logger.class
	            && field.isAnnotationPresent(InjectLogger.class)) {
	        	aTypeEncounter.register(new Slf4jMembersInjector<I>(field));
	        }
	      }
	}
}

Finally, you need the Slf4jMembersInjector, which does the actual injection:

import java.lang.reflect.Field;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.MembersInjector;

public class Slf4jMembersInjector<T>  implements MembersInjector<T> {
	private final Field field;
    private final Logger logger;
    
    Slf4jMembersInjector(Field aField) {
    	field = aField;
    	logger = LoggerFactory.getLogger(field.getDeclaringClass());
    	field.setAccessible(true);
    }
  
	public void injectMembers(T anArg0) {
		try {
			field.set(anArg0, logger);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
}

Now you just need to bind your TypeListener inside your module class:

   bindListener(Matchers.any(), new Slf4jTypeListener());

The actual usage is simple, but instead of @Inject we need to use @InjectLogger :

    @InjectLogger Logger logger;
This entry was posted in guice, java and tagged , , , . Bookmark the permalink.

2 Responses to Logging with SLF4J and Guice

  1. Pingback: Java.util.logging vs. slf4j « Software Development and more …

  2. ceki says:

    Thank you for this informative post. Just linked to it from http://www.slf4j.org/docs.html

    Cheers.

Leave a Reply