Is there an easy way to create a logger instance f

2019-05-04 22:54发布

问题:

I am now using static method to log (because I find it very easy to log in Android), but now I need to configure different appenders for different classes, so I have a problem with static logging method.

And I read Log4J: Strategies for creating Logger instances, I notice it is time for me to change the logging strategy, so I will need the following code to my class which needs a special logger.

private static final Logger logger  =  Logger.getLogger(XXXX.class);

I feel since this is repeated job, so is there any ways for us to avoid adding repeated code to every single class, or just write less code. Will AOP or dependency injection can do such jobs?

I am using log4j and Spring, but any other ways to do that will be highly appreciated.

回答1:

Yes you could.

  • Create your customized annotation say @Logger

    @Retention(RUNTIME)//will be used at runtime, so retain till runtime
    @Target(FIELD)//field level annotation
    public @interface Logger {
    }
    
  • Annotate your Logger field as @Logger in the beans which get's injected by Spring.

    @Logger
    private Logger logger;
    

    Here you could use slf4j logger but upto you as you can directly use log4j etc. See how i annotated logger here. This is within the bean which gets created by Spring.

  • Use of Spring lifecycle interface called BeanPostProcessor and in method postProcessBeforeInitialization, using reflection utils class, to inject the logger.

    public class LoggerInjector implements BeanPostProcessor {
    
    /**
     * Return the bean itself.
    */
    public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
        return bean;
    }
    
    /**
     * For all beans before initialization, inject the logger using slf4j.
     * @param bean
     * @param beanName
     * @return returns same bean by injecting logger.
     */
    public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
        ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
            public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
                ReflectionUtils.makeAccessible(field);
                if (field.getAnnotation(Logger.class) != null) {
                    if (field.get(bean) == null) {
                        final Logger logger = LoggerFactory.getLogger(bean.getClass());
                        field.set(bean, logger);
                    }
                }
            }
        });
        return bean;
    }
    }
    
  • Once done, let spring know that you have a logger injector like:

    <bean id="loggerInjector" 
        class="com.mypackage.LoggerInjector"/>
    


回答2:

You can try with Lombok project. You just have to add lombok library into your classpath and ofcourse you need log4j libraries too. and use @Slf4j annotation in class level, then you should be able to write log messages like this. log.info("message") without creating static logger instance.

more on here http://projectlombok.org/features/Log.html



回答3:

It is indeed a "repeated job", to add a logger to each class that needs one -- however, keep in mind that even with dependency injection you would still need to declare method variable ("logger", in your case). So, it would not save you much typing.

I would also recommend that you use a logging facade, like SLF4J, with an underlying implementation, like logback. Logback was written by the same author of log4j, but is better for these reasons. If you do choose this route (of using SLF4J with your app), remember to add jcl-over-slf4j to your classpath (probably in your pom file), since you're using Spring.

Again regarding logging strategies, it may be worth looking at the demo application referenced in the Logback documentation. Example code, much like yours, only using the logging facade:

Logger logger = LoggerFactory.getLogger(LoginAction.class);


回答4:

It is possible to add a logger bean in your context using the factory-method for each logger you need (although it is not the usual way):

<bean id="logger" scope="prototype" class="org.apache.log4j.Logger" factory-method="getLogger">
    <constructor-arg name="name" value="youLoggerName" />
</bean>

then you can simply inject your logger:

@Autowired
private Logger logger;