Keeping your logs clean with Apache Tomcat 9, log4j2 and spring-boot
These last days I've been playing with Apache Tomcat 9 and log4j2. You may wonder why I want to change the good an old Tomcat JULI. Well, although I love the simplicity of its configuration and the fact that JULI works, log4j2 offers a big variety of appenders that make it very interesting. Also I prefer the way of how log4j2 rolls the files, keeping the original name un-touched (e.g. catalina.log), this makes my log ingestion configuration (logstash) a bit simpler.
Apache Tomcat with Log4j2 Configuration
The Apache-Tomcat configuration is quite simple. You just need to add the log4j2-api, log4j2-core and log4j2-appserver libraries into the Tomcat classpath, provide the log4j2 configuration file and remove the $CATALINA_BASE/conf/logging.properties from your installation. These steps are described here.
If you are using tomcat versions prior than 8.5.4 you may wonder what's happened with the juli-adapter libraries from the famous tomcat extras? They are not needed anymore, because log4j2 can be plugged direcly into java.util.logging, hooray! See more at ASF Bugzilla – Bug 58588
Logging configuration for production environments
If you are running a recent Tomcat version you can see that the "Using Log4j" chapter of previous versions is not still there. You may have the temptation of re-using the old log4j.properties that mirrors the default java.util.logging. Be careful, cause the log4j2 properties syntax has changed!!! With this one (BTW, thanks Bryan Maupin!!!) you will get the default configuration with the three default tomcat loggers and appenders. For a production usage you may take into account the tomcat docs considerations and the recommendations about logging from Mark Thomas, member of the Apache Tomcat Project Management Committee:
- Do not duplicate the tomcat output in the catalina.out (console) and in the catalina.log, getting rid of the ConsoleHandler
- Add rotation to your catalina.log
- Removing the non used appenders manager.log and host_manager.log
- Join different loggers (CATALINA & LOCALHOST) in one appender (catalina.log). This is useful to spot deployment problems in the applications like wrongly defined filters.
The log4j2 configuration provided in the example tries to follow those considerations. If you start the tomcat server with it, and your tomcat is running "healthy", you should see almost nothing in the console. With this configuration, in the console you will see only very catastrophic things like OutOfMemoryErrors, or thread dumps.
Forcing System.out and System.err to log
If we deploy applications in our server that still uses System.out and/or System.err, we can force them to use our logger adding swallowOutput="true" in the default $CATALINA_BASE/conf/context.xml of the server. We can do this as well per application, modifying each of their META-INF/context.xml.
Java Util Logging and Log4j2
Also we can have applications that use the good and old JUL framework:
Apr 01, 2019 9:22:05 AM hello.HelloWorldController sayHello INFO: Hello, JUL!!!
These applications will use the default logging.properties of our JDK, which default level is INFO and format the SimpleFormatter. We can force them use our log4j2 format changing the environment variable LOGGING_MANAGER. You can do this adding in the setenv.sh file: LOGGING_MANAGER="-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager" Now your logs will look like something like this:
TOMCAT 2019-04-01 09:13:53,524 [Catalina-utility-1] INFO hello.HelloWorldApplication- Hello, JUL!!!
Remember that org.apache.logging.log4j.jul.LogManager is included in the log4j-jul-2.11.2.jar bridge which must be added to your classpath.
Spring Boot and log4j2
We want to deploy a spring-boot hello, REST application that uses our log4j2 configuration (appender and format) and at the same time we want to keep the ability of definining different loggers. For instance lets imagine that we want to set the debug level to our business classes, the hello package, and to the springframework ones. And also lets silent the classic spring banner:
logging.level.org.springframework=debug logging.level.hello=debug spring.main.banner-mode=off
By default the spring-boot starters use logback, so for using log4j2 we have to exclude spring-boot-starter-logging from the different starter artifacts and add the spring-boot-starter-log4j2 dependency. This last one will include all the log4j2 dependencies plus the SLF4J ones. It may happen that we are not using any of the sl4j features, like parametrized messages, or like in our case we want to use directly log4j2. If this is your case you can just add the log4j-web dependency. By the way this is the one that you have to add for non spring web applications, see more at "Using Log4j in Web Applications".
In this entry we have learned how to configured Apache Tomcat to work with log4j2, how to make this setup ready for production and how to deploy a spring-boot application in our tomcat server that makes use of our configuration. You can find all of this in this github repository.
Have a nice coding day!