Как в Spring logger получить

Разрабатывая приложения используя IoC-контейнер Spring думаю каждый задумывался, а как же «правильнее и красивее» создать логгер. В данной публикации хочу привести несколько примеров решения данной задачи.

Решение 1


Получаем логгер напрямую через LoggerFactory:

@Component
public class MyBean {
    private static final Logger log = LoggerFactory.getLogger("application");
    ...
}


Данное решение является классическим, безусловно работающим, но нарушает саму идеологию IoC, ведь нам хочется, что бы работу по созданию логгера выполнил сам контейнер.

Решение 2


Получаем логгер из контейнера при помощи Autowired:

@Component
public class MyBean {
    @Autowired
    private Logger log;
    ...
}


Для этого в конфигурации Spring объявляем Bean:

@EnableAutoConfiguration
@ComponentScan
public class ApplicationConfiguration {
    @Bean
    public Logger logger(){
        return LoggerFactory.getLogger("application");
    }
...
}


В данном решении задача по созданию логгера возложена на сам контейнер и укладывается в идеологию IoC, но что же делать, если логгеров в приложении должно быть больше одного?

Решение 3


Объявляем каждый логгер в виде отдельного 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");
    }
...
}


Получаем нужный логгер используя соответствующий Qualifier:

@Component
public class MyBean {
    @Autowired
    private Logger log;
    @Autowired
    @Qualifier("loggerBean")
    private Logger log2;
    ...
}


Данное решение является достаточным в большинстве случаев, и использует только готовые средства контейнера. Одним из минусов данного решения является то, что при добавлении нового логгера всегда придется объявлять новый Bean. Есть ли более универсальный способ?

Решение 4


Получаем логгер из контейнера при помощи специальной аннотации, назовем ее Logging:

@Component
public class MyBean {
    @Logging
    private Logger log;
    @Logging("loggerBean")
    private Logger log2;

    ...
}


Для это собственно необходимо объявить аннотацию:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
    String value();
}


Данная аннотация будет указывать контейнеру на то, что необходим логгер с именем переданным в параметр value. Если данный параметр не указан, то логгер будет получен по классу компонента, в нашем случае MyBean.
Отлично, но контейнер не умеет обрабатывать нашу аннотацию. Давайте его научим, для этого создадим процессор:

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;
    }

}


И объявим процессор в конфигурации Spring:

@EnableAutoConfiguration
@ComponentScan
public class ApplicationConfiguration {
    @Bean
    public LoggingAnnotationProcessor loggingAnnotationProcessor(){
        return new LoggingAnnotationProcessor();
    }
...
}


Данное решение является более универсальным, но необходимо дополнительно объявить аннотацию и написать для нее процессор.

Заключение


Друзья, предлагай в комментариях ваши варианты решения данной задачи, буду очень рад!

Исходный код примеров доступен на GitHub

© Habrahabr.ru