Spring Inject Logger by Annotation Example
This tutorial: Spring Inject Logger by Annotation Example show you how to Inject a logger by Annotation.
Dependencies
We use slf4j logger interface and log4j logger implementation. Next add the spring dependencies to your project.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.spring.core</groupId>
<artifactId>inject-logger</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>https://memorynotfound.com</url>
<name>SPRING - ${project.artifactId}</name>
<properties>
<slf4j.version>1.7.12</slf4j.version>
<log4j.version>2.3</log4j.version>
<spring.version>4.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<!-- logging interface -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- Binding for Log4J -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- Log4j API and Core implementation required for binding -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
</project>
Spring Java Configuration
We use Spring Java Configuration to configure our spring beans.
package com.memorynotfound;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.memorynotfound")
public class AppConfig {
}
Create @Log Annotation
Create a new @Log
annotation.
package com.memorynotfound;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(RUNTIME)
@Target(FIELD)
@Documented
public @interface Log {
}
Spring Inject Logger by Annotation
We can use the @Log
annotation at field level to describe that we want to inject a logger.
package com.memorynotfound;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
@Component
public class StepService {
private static @Log Logger LOG;
public void step(int steps){
LOG.info("executing " + steps + " steps.");
}
}
Create LogInjector
This is where the magic happens. We 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
.
package com.memorynotfound;
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;
import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Field;
@Component
public class LogInjector implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessBeforeInitialization(final Object bean, String name) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// make the field accessible if defined private
ReflectionUtils.makeAccessible(field);
if (field.getAnnotation(Log.class) != null) {
Logger log = LoggerFactory.getLogger(bean.getClass());
field.set(bean, log);
}
}
});
return bean;
}
}
Log4j2 properties
Add the log4j2.xml to your classpath.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration xmlns="http://logging.apache.org/log4j/2.0/config">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%-5p | %d{yyyy-MM-dd HH:mm:ss} | [%t] %C{2} (%F:%L) - %m%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="com.memorynotfound" level="debug"/>
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>
Bootstrap Application
Lets test our application. First we bootstrap spring configuration. Next we test the service.
package com.memorynotfound;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String...args){
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
StepService stepService = context.getBean(StepService.class);
stepService.step(1);
stepService.step(2);
stepService.step(3);
}
}
Output
INFO | 2015-06-23 21:16:58 | [main] memorynotfound.StepService (StepService.java:12) - executing 1 steps.
INFO | 2015-06-23 21:16:58 | [main] memorynotfound.StepService (StepService.java:12) - executing 2 steps.
INFO | 2015-06-23 21:16:58 | [main] memorynotfound.StepService (StepService.java:12) - executing 3 steps.
Injecting is really nice, but I suggest to add default or null value for LOG field before injection e.g.
class Component {
…. @Log LOG = Logger.NULL_LOGGER;
}
This way component may be easily unit tested and e.g. borrowed to be used in console app.
Good tip, thanks for sharing
Nice. One question.
private static @Log Logger LOG;
Why has it to be static?
It’s not a requirement, but a good idea. This way you only need to inject the logger once and have all of the instances share the same logger. This can reduce memory consumption and cpu cycles in large applications.