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;
Pingback: Java.util.logging vs. slf4j « Software Development and more …
Thank you for this informative post. Just linked to it from http://www.slf4j.org/docs.html
Cheers.