Using java annotation to inject logger dependency - java

I am using spring with aspect-j annotation support to allow for an #Loggable annotation. This allows automatic logging on a class based on the configuration.
I am wondering if I can somehow use this annotation to expose an slf4j Logger variable into the class for direct use, so that I don't have to do something to the effect of:
Logger logger = LoggerFactory.getLogger(MyClass.class);
It would be nice if the above was implicitly available due to the annotation and I could just go about doing logger.debug("..."); without the declaration. I'm not sure if this is even possible.

You can use the BeanPostProcessor interface, which is called by the ApplicationContext for all created beans, so you have the chance to fill the appropriate properties.
I created a simple implementation, which does that:
import java.lang.reflect.Field;
import java.util.List;
import net.vidageek.mirror.dsl.Mirror;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
#Component
public class LoggerPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
List<Field> fields = new Mirror().on(bean.getClass()).reflectAll().fields();
for (Field field : fields) {
if (Logger.class.isAssignableFrom(field.getType()) && new Mirror().on(field).reflect().annotation(InjectLogger.class) != null) {
new Mirror().on(bean).set().field(field).withValue(LoggerFactory.getLogger(bean.getClass()));
}
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
You don't have to do any complex registration step, since the ApplicationContext is capable of recognizing BeanPostProcessor instances and automatically register them.
The #InjectLogger annotation is:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface InjectLogger {
}
And then you can easily use the annotation:
public static #InjectLogger Logger LOGGER;
...
LOGGER.info("Testing message");
I used the Mirror library to find the annotated fields, but obviously you may perform a manual lookup in order to avoid this additional dependency.
It's actually a nice idea to avoid repeated code, and even small issues that come from copying and paste the Logger definitions from other classes, like when we forget to change the class parameter, which leads to wrong logs.

You can't do it with an aspect, but lombok can help you in a, in my opinion, elegant way. See #Log annotation.

I think the solution from #Redder is a great way of doing this. However, I didn't want to include the Mirror library so I wrote an implementation of LoggerPostProcessor that uses the Java reflect library instead. Here it is:
package com.example.spring.postProcessor;
import com.example.annotation.InjectLogger;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
#Component
public class LoggerPostProcessor implements BeanPostProcessor {
private static Logger logger = LoggerFactory.getLogger(LoggerPostProcessor.class);
/* (non-Javadoc)
* #see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization(java.lang.Object, java.lang.String)
*/
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
List<Field> fields = Arrays.asList(bean.getClass().getDeclaredFields());
for (Field field : fields) {
if (Logger.class.isAssignableFrom(field.getType()) && field.getAnnotation(InjectLogger.class) != null) {
logger.debug("Attempting to inject a SLF4J logger on bean: " + bean.getClass());
if (field != null && (field.getModifiers() & Modifier.STATIC) == 0) {
field.setAccessible(true);
try {
field.set(bean, LoggerFactory.getLogger(bean.getClass()));
logger.debug("Successfully injected a SLF4J logger on bean: " + bean.getClass());
} catch (IllegalArgumentException e) {
logger.warn("Could not inject logger for class: " + bean.getClass(), e);
} catch (IllegalAccessException e) {
logger.warn("Could not inject logger for class: " + bean.getClass(), e);
}
}
}
}
return bean;
}
/* (non-Javadoc)
* #see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization(java.lang.Object, java.lang.String)
*/
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}

I want to make some improvements to #Redder's solution.
First - we can omit introduction of new annotation #Log, instead we
can use Spring's #Autowired annotation with 'required' flag set to
'false' to make Spring not to check that bean was injected or not
(because, we will inject it later).
Second - use Spring's ReflectionUtils API that provides all
needed methods for field discovering and manipulation, so we don't need additional external dependencies.
Here an example (in Java 8, but can be rewritten in Java 7/6/etc., also slf4j facade is used but it can be replaced with any other logger):
#Component
public class LoggerPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Logger logger = getLogger(bean.getClass());
doWithFields(bean.getClass(), field -> {
makeAccessible(field);
setField(field, bean, logger);
}, field -> field.isAnnotationPresent(Autowired.class) && Logger.class.equals(field.getType()));
return bean;
}
...
}
...
//logger injection candidate
#Autowired(required = false)
private Logger log;

Since I got this as the first result when trying to do the same thing in CDI (JSR 299: Context and Dependency Injection), this link shows the straightforward way to do this using CDI (and also an alternative using Spring):
Basically, you only need to inject:
class MyClass {
#Inject private Log log;
And have a logger factory like so:
#Singleton
public class LoggerFactory implements Serializable {
private static final long serialVersionUID = 1L;
static final Log log = LogFactory.getLog(LoggerFactory.class);
#Produces Log createLogger(InjectionPoint injectionPoint) {
String name = injectionPoint.getMember().getDeclaringClass().getName();
log.debug("creating Log instance for injecting into " + name);
return LogFactory.getLog(name);
}
}
I found that I needed to add transient to the injected log so that I did not get a passivating scope exception in my session scoped beans:
#Named()
#SessionScoped()
public class MyBean implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private transient Log log;

Herald provides a very simple BeanPostProcessor which does all the magic for you. You can annotate any field of Spring bean with a #Log annotation to let Herald inject suitable logger in this field.
Supported logging frameworks:
JavaTM 2 platform's core logging framework
Apache Commons Logging
Simple Logging Facade for Java (SLF4J)
SLF4J Extended logger
Logback
Apache Log4j
Apache Log4j 2
JBoss Logging
Syslog4j
Syslog4j fork from Graylog
Fluent Logger for Java
It is also possible to add other logging frameworks.
Github repo: https://github.com/vbauer/herald

Related

Dynamically logging in spring framework

Currently I'm having a spring boot application and for each class like SampleClass that I would like to have a log I need to initialize the logger like this:
private static Logger log = LoggerFactory.getLogger(SampleClass.class);
Which means that we need statically send the classname to the getLogger method.
I was thinking of creating a loggable interface and everytime a class implements this interface, it dynamically finds the class name and properly write the log to the outputstream with proper classname.
I googled to find a proper solution but every example is specifically sending the classname in the compile time. Does spring have this ability?
I recommend to use Lombok #log varient in your project, it will automatically set at compile time
If you are using slf4j use #Slf4j
It's best approach and even spring uses internally in some projects
Create a new #Log annotation.
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Log {}
Now implement the BeanPostProcessor which gives us the postProcessBeforeInitialization method which allows us to manage a bean before initialization. We search for the #Log annotation and inject an implementation with the LoggerFactory.
#Component
public class LoggerPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
Logger log = LoggerFactory.getLogger(bean.getClass());
ReflectionUtils.makeAccessible(field);
field.set(bean, log);
}
}, new ReflectionUtils.FieldFilter() {
#Override
public boolean matches(Field field) {
return field.isAnnotationPresent(Log.class);
}
});
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
And use the #Log annotation at field level to describe that we want to inject a logger.
#Log
private Logger logger;

Multiple #ConfigurationProperties validator beans in Spring environment

When using the #ConfigurationProperties annotation to inject properties into a bean, Spring provides the ability to define a custom validator to validate those properties.
The ConfigurationPropertiesBindingPostProcessor looks up this validator using the fixed bean name "configurationPropertiesValidator" and class org.springframework.validation.Validator.
Now assume I have a #ConfigurationProperties with its validator in a module A. Another module B has a dependency on module A. Module B also defines its own #ConfigurationProperties and its own validator.
When the app loads up, the post-processor picks up only one of these beans. This disables the other part of the validation.
Is there a solution to this? How can I keep both configuration property validators enabled in my application?
I just encountered the same problem and realized that ConfigurationPropertiesBindingPostProcessor verifies if the class annotated with #ConfigurationProperties implements the Validator interface itself. (see org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#determineValidator)
So the solution is to move all property validation to the annotated property class:
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
#ConfigurationProperties("test.properties")
#Component
public class TestProperties implements Validator {
private String myProp;
public String getMyProp()
{
return myProp;
}
public void setMyProp( String myProp )
{
this.myProp = myProp;
}
public boolean supports( Class<?> clazz )
{
return clazz == TestProperties.class;
}
public void validate( Object target, Errors errors )
{
ValidationUtils.rejectIfEmpty( errors, "myProp", "myProp.empty" );
TestProperties properties = (TestProperties) target;
if ( !"validThing".equals( properties.getMyProp() ) ) {
errors.rejectValue( "myProp", "Not a valid thing" );
}
}
}

Manual bean registration and configuration order

I had an issue with spring's #Order annotation, it seems i can't get it to work in my application. Hence I managed to create a test class that imitates the very same behaviour which #Order does not have any effect on my components. The following test fails to run because of lack of bean typed javax.sql.Datasource:
package com.so;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
AService aService;
}
#Repository
#Order(Ordered.LOWEST_PRECEDENCE)
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
#Order(Ordered.LOWEST_PRECEDENCE)
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
// #Component does not have any effect
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE)
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
Manual bean registration has risks as stated here: https://stackoverflow.com/a/11751503/1941560, although I cannot find out in which circumtances that #Order annotation works. For above configuration of application I expect the execution order like; RepoConf, AConf, ADAO, AService.
A weird thing to notice is that when I changed the order of component classes declared to (with commencing array with RepoConf):
Class<?>[] classes = new Class[]{RepoConf.class, AConf.class, ADAO.class, AService.class};
or changed my AConf class to:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE + 100)
public static class AConf {
#Autowired
RepoConf repoConf; // must be declared before aService
#Autowired
AService aService;
}
application works as expected. Could someone explain that spring container's behaviour and how can I utilize #Order annotations?
springframework version I use is 4.2.1.RELEASE
Judging by the JavaDoc documentation for the #Order annotation, I don't think it is used to order bean creation:
NOTE: Annotation-based ordering is supported for specific kinds of
components only — for example, for annotation-based AspectJ aspects.
Ordering strategies within the Spring container, on the other hand, are
typically based on the Ordered interface in order to allow for
programmatically configurable ordering of each instance.
Consulting the Spring Framework documentation, the #Ordered annotation seems to be used for:
Ordering of instances when injected into a collection
Ordering the execution of event listeners
Ordering of #Configuration class processing, for example if you want to override a bean by name.
From the documentation, it does not look like #Order is intended for your use case.
However, from the Spring documentation, we can see there depends-on, which enforces that certain beans should be created before the bean being defined. This has a corresponding annotation #DependsOn.
It looks that your version of Spring Framework simply ignores the #Order annotation on configuration classes. There's no surprise here, because that annotation should only be used on classes that implement the Ordered interface. Moreover, I could never find any reference to it about configuration classes in Spring Framework reference documentation.
Anyway you are walking in terra incognita here. It is not described in official documentation, so it will work or not depending on implementation details. What happens here is (seeing the results):
the AnnotationConfigApplicationContext first instantiate the configuration classes and their beans in their declaration order
it then builds all the beans in their instantiation order
As you register the datasource myds at build time in its configuration class, it happens to be registered in time to be autowired in other beans when repoConf is built before any other beans using them. But this is never guaranteed by Spring Framework documentation and future versions could use a different approach without breaking their contract. Moreover, Spring does change initialization order to allow dependencies to be constructed before beans depending on them.
So the correct way here is to tell Spring that the ADAO bean depends on the configuration RepoConf. Just throw away all Order annotations which are no use here, and put a #DependsOn one. You code could be:
package com.so;
...
public class TestSpring {
public static void main(String[] args) {
Class<?>[] classes = new Class[]{AConf.class, ADAO.class, AService.class, RepoConf.class} ;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(classes);
}
#Configuration
public static class AConf {
#Autowired
AService aService;
}
#DependsOn("repoConf")
#Repository
public static class ADAO {
#Autowired
#Qualifier("myds")
DataSource dataSource;
}
#Service
public static class AService {
#Autowired
ADAO adao;
#PostConstruct
public void init() {
System.out.println("service init");
}
}
#Configuration("repoConf")
public static class RepoConf {
#Autowired
BeanFactory beanFactory;
#PostConstruct
public void init() {
ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory;
configurableBeanFactory.registerSingleton("myds", new AbstractDataSource() {
#Override
public Connection getConnection() throws SQLException {
return null;
}
#Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
});
}
}
}
#DependsOn ensures that repoConf will be created before ADAO making the datasource available for dependency injection.

Error on creating custom log4j Appender

I'm trying to create a custom Appender that will persist logs to the database using JPA.
The thing is that I'm using PersistenceContext attribute like this
package com.foobar.logging;
import com.foobar.model.SysLog;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.MDC;
import org.apache.log4j.spi.LoggingEvent;
import javax.ejb.Stateless;
#Stateless
public class LogManager extends AppenderSkeleton {
#PersistenceContext(unitName = "primary")
private EntityManager em;
#Override
protected void append(LoggingEvent le) {
SysLog log = new SysLog();
log.setDescripcion(le.getMessage().toString());
if (MDC.get("IdUsuario") != null) {
log.setIdUsuario(MDC.get("IdUsuario").toString());
}
log.setSysAccionLog(null);
this.em.persist(log);
}
#Override
public void close() {
}
#Override
public boolean requiresLayout() {
return false;
}
}
Now when I'm deploying the WAR to JBoss AS 7.1, it fails, and I get the error:
java.lang.VerifyError: class com.foobar.logging.LogManager$Proxy$_$$_Weld$Proxy$ overrides final method getName.()Ljava/lang/String;
How can I use CDI to inject my EntityManager inside an AppenderSkeleton? Has anyone accomplished JPA persistance in an AppenderSkeleton using CDI?
I also tried not using CDI, but since every other object in my app uses it (JAX-RS classes), it collapses.
EJBs are proxies. AppenderSkeleton has a getName method that is final. I think for your use case, you need to implement Appender directly. This will avoid the bean method getName
However, I have to question the idea of trying to make an appendar an EJB. How are you instantiating it?

Spring constructor injection of SLF4J logger - how to get injection target class?

I'm trying to use Spring to inject a SLF4J logger into a class like so:
#Component
public class Example {
private final Logger logger;
#Autowired
public Example(final Logger logger) {
this.logger = logger;
}
}
I've found the FactoryBean class, which I've implemented. But the problem is that I cannot get any information about the injection target:
public class LoggingFactoryBean implements FactoryBean<Logger> {
#Override
public Class<?> getObjectType() {
return Logger.class;
}
#Override
public boolean isSingleton() {
return false;
}
#Override
public Logger getObject() throws Exception {
return LoggerFactory.getLogger(/* how do I get a hold of the target class (Example.class) here? */);
}
}
Is FactoryBean even the right way to go? When using picocontainers factory injection, you get the Type of the target passed in. In guice it is a bit trickier. But how do you accomplish this in Spring?
Here is an alternative to your solution. You could achieve your goal with BeanFactoryPostProcessor implementation.
Let's assume you want to have a class with logging. Here it is:
package log;
import org.apache.log4j.Logger;
#Loggable
public class MyBean {
private Logger logger;
}
As you could see this class does nothing and created just to be a logger container for simplicity. The only remarkable thing here is #Loggable annotation.
Here its source code:
package log;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Loggable {
}
This annotation is only a marker for further processing. And here is a most interesting part:
package log;
import org.apache.log4j.Logger;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import java.lang.reflect.Field;
public class LoggerBeanFactoryPostProcessor implements BeanFactoryPostProcessor{
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] names = beanFactory.getBeanDefinitionNames();
for(String name : names){
Object bean = beanFactory.getBean(name);
if(bean.getClass().isAnnotationPresent(Loggable.class)){
try {
Field field = bean.getClass().getDeclaredField("logger");
field.setAccessible(true);
field.set(bean, Logger.getLogger(bean.getClass()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
It searches through all beans, and if a bean is marked as #Loggable, it initialize its private field with name logger. You could go even further and pass some parameters in #Loggable annotation. For example, it could be a name of field corresponding to logger.
I used Log4j in this example, but I guess it should work exactly the same way with slf4j.
To make your code more Spring aware use the InjectionPoint to define the loggers, i.e.:
#Bean
#Scope("prototype")
public Logger logger(InjectionPoint ip) {
return Logger.getLogger(ip.getMember().getDeclaringClass());
}
#Scope("prototype") is needed here to create 'logger' bean instance every time method is called.
I resolved it with a custom BeanFactory. If anyone comes up with a better solution, I would be happy to hear it. Anyway, here's the bean factory:
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
public class CustomBeanFactory extends DefaultListableBeanFactory {
public CustomBeanFactory() {
}
public CustomBeanFactory(DefaultListableBeanFactory delegate) {
super(delegate);
}
#Override
public Object resolveDependency(DependencyDescriptor descriptor,
String beanName, Set<String> autowiredBeanNames,
TypeConverter typeConverter) throws BeansException {
//Assign Logger parameters if required
if (descriptor.isRequired()
&& Logger.class.isAssignableFrom(descriptor
.getMethodParameter().getParameterType())) {
return LoggerFactory.getLogger(descriptor.getMethodParameter()
.getDeclaringClass());
} else {
return super.resolveDependency(descriptor, beanName,
autowiredBeanNames, typeConverter);
}
}
}
Example usage with an XML config:
CustomBeanFactory customBeanFactory = new CustomBeanFactory();
GenericApplicationContext ctx = new GenericApplicationContext(customBeanFactory);
XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(ctx);
xmlReader.loadBeanDefinitions(new ClassPathResource("beans.xml"));
ctx.refresh();
EDIT:
Below you can find Arend v. Reinersdorffs improved version (see the comments for an explanation).
import java.lang.reflect.Field;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.MethodParameter;
public class CustomBeanFactory extends DefaultListableBeanFactory {
public CustomBeanFactory() {
}
public CustomBeanFactory(DefaultListableBeanFactory delegate) {
super(delegate);
}
#Override
public Object resolveDependency(DependencyDescriptor descriptor,
String beanName, Set<String> autowiredBeanNames,
TypeConverter typeConverter) throws BeansException {
//Assign Logger parameters if required
if (Logger.class == descriptor.getDependencyType()) {
return LoggerFactory.getLogger(getDeclaringClass(descriptor));
} else {
return super.resolveDependency(descriptor, beanName,
autowiredBeanNames, typeConverter);
}
}
private Class<?> getDeclaringClass(DependencyDescriptor descriptor) {
MethodParameter methodParameter = descriptor.getMethodParameter();
if (methodParameter != null) {
return methodParameter.getDeclaringClass();
}
Field field = descriptor.getField();
if (field != null) {
return field.getDeclaringClass();
}
throw new AssertionError("Injection must be into a method parameter or field.");
}
}
Try something like:
#Component
public class Example {
#Autowired
#Qualifier("exampleLogger")
private final Logger logger;
}
And:
<bean id="exampleLogger" class="org.slf4j.LoggerFactory" factory-method="getLogger">
<constructor-arg type="java.lang.Class" value="package.Example"/>
</bean>
Since Spring 4.3.0 you can use InjectionPoint or DependencyDescriptor as parameters for bean producing methods:
#Component
public class LoggingFactoryBean {
#Bean
public Logger logger(InjectionPoint injectionPoint) {
Class<?> targetClass = injectionPoint.getMember().getDeclaringClass();
return LoggerFactory.getLogger(targetClass);
}
}
Yeah, you are going in the wrong direction. If I were you I would inject the LoggerFactory. If you want to hide that it is slf4j then I'd define a LoggerFactory interface and inject a class which delegates through to slf4j Logger.
public interface LoggerFactory {
public Logger getLogger(Class<?> clazz);
}
...
import org.slf4j.LoggerFactory;
public class Slf4jLoggerFactory implements LoggerFactory {
public Logger getLogger(Class<?> clazz) {
return org.slf4j.LoggerFactory.getLogger(clazz);
}
}
However, before you go there, this is approximately what org.apache.commons.logging is doing right? http://commons.apache.org/logging/
You use Log's instead of Loggers:
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class CLASS {
private Log log = LogFactory.getLog(CLASS.class);
...
Apache then looks through the classpath to see if you have log4j or others and delegates to the "best" one that it finds. Slf4j replaces log4j in the classpath so if you have it loaded (and apache log4j excluded) commons logging will delegate to it instead.
Why are you creating a new logger for each instance? The typical pattern is to have one logger per class (as a private static member).
If you really do want to do it that way: Maybe you can write a logger factory class, and inject that? Something like:
#Singleton
public class LogFactory {
public Logger getLogger(Object o) {
return LoggerFactory.getLogger(o.getClass());
}
}
I am trying to get this feature into official SLF4J API. Please support/vote/contribute: https://issues.jboss.org/browse/JBLOGGING-62
(this feature is already implemented by JBoss Logging + Seam Solder, see http://docs.jboss.org/seam/3/latest/reference/en-US/html/solder-logging.html)
11.4. Native logger API
You can also inject a "plain old" Logger (from the JBoss Logging API):
import javax.inject.Inject;
import org.jboss.logging.Logger;
public class LogService {
#Inject
private Logger log;
public void logMessage() {
log.info("Hey sysadmins!");
}
}
Log messages created from this Logger will have a category (logger name) equal to the fully-qualified class name of the bean implementation class. You can specify a category explicitly using an annotation.
#Inject #Category("billing")
private Logger log;
You can also specify a category using a reference to a type:
#Inject #TypedCategory(BillingService.class)
private Logger log;
Sorry for not providing a relevant answer.

Categories