Convenient logging in SpringBoot + Log4j2 + Maven

Hello, dear Habrovchane.


The material outlined in the article is intended for beginners and, perhaps, as it will save me a few hours of searching on StackOverFlow and other sites in order to get a convenient logging system, which itself will understand where to log - to the console, file or logstash.


At the start of the project, there is always a task to properly set up logging, while on the local environment, the logs should be output to the console and to the file for convenience during the debugging process, and on a remote server, logging to the console is extremely undesirable, but instead they should be written to the file, logstash or in db. And if you have Windows locally, and on a remote Linux machine, then the address of the location of this log file is different.


Thus, there are many situations in which you need to constantly change the configuration of the logging system with your hands, depending on external circumstances.


Constantly remembering and commenting on appenders at the time I was bored and I developed a way to customize Log4j2 so that, depending on the Maven-profile chosen, only the necessary appenders are automatically included.


Below is a guide to setting up a project using Spring Boot + Maven + Log4j2, the result of which will be a customized logging system and two appenders: CONSOLE and SOCKET.


First of all, we will make changes to the Maven configuration (pom.xml):
We add the host address for the logstash-appender to the variables at the level of the entire pom.xml:


<properties> <logstash.host>logstashcsm.example.ru</logstash.host> </properties> 

Add the necessary dependencies to work:


 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> </dependencies> 

Please note that we exclude the spring-boot-starter-logging dependency from spring-boot-starter-web.


Configure Maven profiles to dynamically manage connected appenders. At the level of each profile, we set the variables logstash.port, logger.console.tresholdFilter, logger.socket.tresholdFilter .


 <profiles> <profile> <id>local</id> <properties> <logstash.port>10000</logstash.port> <logger.console.tresholdFilter>ALL</logger.console.tresholdFilter> <logger.socket.tresholdFilter>OFF</logger.socket.tresholdFilter> </properties> </profile> <profile> <id>dev</id> <properties> <logstash.port>10001</logstash.port> <logger.console.tresholdFilter>OFF</logger.console.tresholdFilter> <logger.socket.tresholdFilter>ALL</logger.socket.tresholdFilter> </properties> </profile> </profiles> 

logstash.port - the port to which you need to send logs.
logger.console.tresholdFilter - the value sets the filtering level of logs output to the console. In our case, ALL means that all level logs will be output to the console appender.
logger.socket.tresholdFilter - the value sets the filtering level of logs that are sent to logstash. OFF - means that no records sent to this appender will not pass.


Now we need to make changes to application.properties so that from the Log4j2.xml file we can access the value of the variables specified in pom.xml:


logstash.host=@logstash.host@
logstash.port=@logstash.port@
logger.console.tresholdFilter=@logger.console.tresholdFilter@
logger.socket.tresholdFilter=@logger.socket.tresholdFilter@


And finally, we set up the configuration of Log4j2 itself in the log4j2.xml file:


 <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Properties> <Property name="socket.host">${bundle:application:logstash.host}</Property> <Property name="socket.port">${bundle:application:logstash.port}</Property> <Property name="console.thresholdFilter">${bundle:application:logger.console.tresholdFilter}</Property> <Property name="socket.thresholdFilter">${bundle:application:logger.socket.tresholdFilter}</Property> </Properties> <Appenders> <Console name="CONSOLE" target="SYSTEM_OUT"> <ThresholdFilter level="${console.thresholdFilter}"/> <PatternLayout pattern="%d %-5p [%t] %c{10} - %m%n"/> </Console> <Socket name="SOCKET" host="${socket.host}" port="${socket.port}" immediateFlush="true"> <ThresholdFilter level="${socket.thresholdFilter}"/> <JSONLayout eventEol="true" compact="true"/> </Socket> <Async name="ASYNC"> <AppenderRef ref="CONSOLE"/> <AppenderRef ref="SOCKET"/> </Async> </Appenders> <Loggers> <Logger name="ru.example" level="debug" additivity="false"> <AppenderRef ref="ASYNC"/> </Logger> <Root level="error"> <AppenderRef ref="ASYNC"/> </Root> </Loggers> </Configuration> 

Now, to call a logger in its class, you need to create an instance of it:


 private static Logger logger = LoggerFactory.getLogger(YourClass.class); 

and contact him:


 logger.info("   "); 

That's all. Now logging will work taking into account the Maven-profile active in the project.
Please note that in the listing of the log4j2.xml file, two loggers are ru.example - ru.example and root , and they have different levels of event logging. The first will work for all events generated by the classes from the ru.example.* Package and ru.example.* everything from the DEBUG level, and root logger will record events from absolutely all classes, but from the ERROR level.
At the same time, in order to not duplicate entries in the logs, the additivity="false" setting is used.

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


All Articles