📜 ⬆️ ⬇️

How to get a Spring logger

While developing applications using the Spring IoC container, I think everyone thought about how to create a logger “more correctly and more beautifully”. In this publication I want to give a few examples of solving this problem.

Solution 1


We get the logger directly through the LoggerFactory:
@Component public class MyBean { private static final Logger log = LoggerFactory.getLogger("application"); ... } 

This solution is a classic, certainly working, but violates the IoC ideology itself, because we want the container itself to do the work on creating a logger.

Solution 2


Get the logger from the container using Autowired:
 @Component public class MyBean { @Autowired private Logger log; ... } 

To do this, in the Spring configuration, declare a Bean:
 @EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean public Logger logger(){ return LoggerFactory.getLogger("application"); } ... } 

In this solution, the task of creating a logger is assigned to the container itself and fits into the IoC ideology, but what to do if there are more than one logger in the application?

Solution 3


We declare each logger as a separate Bean:
 @EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean @Primary public Logger logger(){ return LoggerFactory.getLogger("application"); } @Bean(name = "loggerBean") public Logger loggerBean(){ return LoggerFactory.getLogger("loggerBean"); } ... } 

We get the desired logger using the appropriate Qualifier:
 @Component public class MyBean { @Autowired private Logger log; @Autowired @Qualifier("loggerBean") private Logger log2; ... } 

This solution is sufficient in most cases, and uses only ready-made container means. One of the drawbacks of this solution is that when adding a new logger, you always have to declare a new Bean. Is there a more universal way?
')

Solution 4


Get the logger from the container with a special annotation, let's call it Logging:
 @Component public class MyBean { @Logging private Logger log; @Logging("loggerBean") private Logger log2; ... } 

To do this, you actually need to annotate:
 @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Logging { String value(); } 

This annotation will indicate to the container that a logger is required with the name passed to the value parameter. If this parameter is not specified, then the logger will be obtained by the component class, in our case MyBean.
Great, but the container can't handle our annotation. Let's teach him, for this we will create a processor:
 public class LoggingAnnotationProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) { Class clazz = bean.getClass(); do { for (Field field : clazz.getDeclaredFields()) { Logging annotation = field.getAnnotation(Logging.class); if (annotation!= null) { boolean accessible = field.isAccessible(); field.setAccessible(true); try { if(!annotation.value().isEmpty()){ field.set(bean, LoggerFactory.getLogger(annotation.value())); } else { field.set(bean, LoggerFactory.getLogger(clazz)); } } catch (IllegalAccessException e) { LoggerFactory.getLogger(this.getClass()).error(e.getMessage(), e); } field.setAccessible(accessible); } } clazz = clazz.getSuperclass(); } while (clazz != null); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } } 

And we will declare the processor in the Spring configuration:
 @EnableAutoConfiguration @ComponentScan public class ApplicationConfiguration { @Bean public LoggingAnnotationProcessor loggingAnnotationProcessor(){ return new LoggingAnnotationProcessor(); } ... } 

This solution is more universal, but you must additionally declare an annotation and write a processor for it.

Conclusion


Friends, suggest in the comments your solutions to this problem, I will be very happy!

Sample source code is available on GitHub

Source: https://habr.com/ru/post/276729/


All Articles