Spring Boot, logback and logging.config property - java

I am implementing logging in a Spring Boot project with logback library. I want to load different logging configuration files according to my Spring profiles (property spring.pofiles.active). I have 3 files:
logback-dev.xml
logback-inte.xml
logback-prod.xml
I am using Spring Boot version 1.2.2.RELEASE.
As you can read in Spring Boot documentation:
The various logging systems can be activated by including the appropriate libraries on the classpath, and further customized by providing a suitable configuration file in the root of the classpath, or in a location specified by the Spring Environment property logging.config. (Note however that since logging is initialized before the ApplicationContext is created, it isn’t possible to control logging from #PropertySources in Spring #Configuration files. System properties and the conventional Spring Boot external configuration files work just fine.)
So I tried to set logging.config property in my application.properties file:
logging.config=classpath:/logback-${spring.profiles.active}.xml
But when i start my application, my logback-{profile}.xml is not loaded.
I think logging is a common problem that all projects using Spring Boot have encountered. Am I on the right track with the above approach?
I have other solutions that work, but I find them not as elegant (conditional parsing with Janino in logback.xml file or command line property).

I found a solution and I understood why Spring doesn't use my logging.config property defined in the application.properties file.
Solution and explanation
When initializing logging, Spring Boot only looks in classpath or environment variables.
The solution I used was to include a parent logback.xml file that included the right logging config file according to the Spring profile.
logback.xml
<configuration>
<include resource="logback-${spring.profiles.active}.xml"/>
</configuration>
logback-[profile].xml (in this case, logback-dev.xml) :
<included>
<!-- put your appenders -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{ISO8601} %p %t %c{0}.%M - %m%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- put your loggers here -->
<logger name="org.springframework.web" additivity="false" level="INFO">
<appender-ref ref="CONSOLE" />
</logger>
<!-- put your root here -->
<root level="warn">
<appender-ref ref="CONSOLE" />
</root>
</included>
Note
spring.profiles.active has to be set in command line arguments when starting the app.
Example for JVM properties: -Dspring.profiles.active=dev
Reference documentation
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html
http://docs.spring.io/spring-boot/docs/0.5.0.M3/api/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.html
Edit (multiple active profiles)
In order to avoid multiple files, we could use conditional processing which requires Janino dependency (setup here), see conditional documentation.
With this method, we can also check for multiple active profiles at the same time. E.g (I did not test this solution, so please comment if it does not work):
<configuration>
<if condition='"${spring.profiles.active}".contains("profile1")'>
<then>
<!-- do whatever you want for profile1 -->
</then>
</if>
<if condition='"${spring.profiles.active}".contains("profile2")'>
<then>
<!-- do whatever you want for profile2 -->
</then>
</if>
<!-- common config -->
</configuration>
See #javasenior answer for another example of a conditional processing.

Another approach that could handle multiple profiles is to create a separate properties file for each environment.
application-prod.properties
logging.config=classpath:logback-prod.xml
application-dev.properties
logging.config=classpath:logback-dev.xml
application-local.properties
logging.config=classpath:logback-local.xml
BE AWARE
If you aren't careful you could end up logging somewhere unexpected
-Dspring.profiles.active=local,dev //will use logback-dev.xml
-Dspring.profiles.active=dev,local //will use logback-local.xml

Instead of adding separate logback xmls for each profile or having the IF condition , I would suggest the following (If you have less difference in the xmls') for easy conditional processing
Documentation link here:
<springProfile name="dev">
<logger name="org.sample" level="DEBUG" />
</springProfile>
<springProfile name="prod">
<logger name="org.sample" level="TRACE" />
</springProfile>

Conditional processing with logback will be a solution without many logback files. Here is a link and a sample logback configuration with spring profiles.
<configuration>
<property name="LOG_LEVEL" value="INFO"/>
<if condition='"product".equals("${spring.profiles.active}")'>
<then>
<property name="LOG_LEVEL" value="INFO"/>
</then>
<else>
<property name="LOG_LEVEL" value="ERROR"/>
</else>
</if>
.
.
appender, logger tags etc.
.
.
<root level="${LOG_LEVEL}">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Also, you might have to add this to your pom.xml
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.6</version>
</dependency>

Spring has support of next tag <springProperty/> inside Logback XML file, this tag described here . It means that you can easily add variable from Spring property file, even this variable value resolves from environment/system variable by Spring.

You can specific different logback.xml for different profile, only 3 steps:
1, Specify actived profile in application.properties or application.yml:
spring.profiles.active: test
2, Config logback to include different configuration by profile:
<!DOCTYPE configuration>
<configuration scan="true" scanPeriod="30 seconds">
<springProperty scope="context" name="profile" source="spring.profiles.active"/>
<include resource="logback.${profile}.xml"/>
</configuration>
3, Create configuration file logback.test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<included>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<root level="INFO"/>
</included>
It's very simple, don't need do anything else.

Related

Add logback filter to included appender

In a Spring Boot application, we take advantage of the pre-configured Logback configuration from Spring by including these configuration files and then just configure the loggers, something like this:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<logger name="com.bla.bla" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>
Now I would like to add a ch.qos.logback.core.filter.EvaluatorFilter to do some filtering, but I cannot figure out if it's possible to add this to the appender defined in the included files, since it looks like the filter must be attached to the appender configuration (according to the Logback documentation). Ideally I would like to have the filter configuration in my application configuration and not touch the pre-defined configuration from Spring, something like this:
<filter class="ch.qos.logback.core.filter.EvaluatorFilter">
<evaluator> <!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
<expression>return logger.equals("org.hibernate.engine.jdbc.spi.SqlExceptionHelper") && message.contains("duplicate key value violates unique constraint \"source_data_version\"");</expression>
</evaluator>
<OnMismatch>NEUTRAL</OnMismatch>
<OnMatch>DENY</OnMatch>
</filter>
Is this possible somehow or do I really need to re-define the appender so to speak in my own configuration file?
I started looking into writing my own filter class, but it doesn't seem to help with this configuration issue, since the custom filter still needs to be added to the appender as far as I understand.

Spring Boot: Enable logging in file in production environment only [duplicate]

I am implementing logging in a Spring Boot project with logback library. I want to load different logging configuration files according to my Spring profiles (property spring.pofiles.active). I have 3 files:
logback-dev.xml
logback-inte.xml
logback-prod.xml
I am using Spring Boot version 1.2.2.RELEASE.
As you can read in Spring Boot documentation:
The various logging systems can be activated by including the appropriate libraries on the classpath, and further customized by providing a suitable configuration file in the root of the classpath, or in a location specified by the Spring Environment property logging.config. (Note however that since logging is initialized before the ApplicationContext is created, it isn’t possible to control logging from #PropertySources in Spring #Configuration files. System properties and the conventional Spring Boot external configuration files work just fine.)
So I tried to set logging.config property in my application.properties file:
logging.config=classpath:/logback-${spring.profiles.active}.xml
But when i start my application, my logback-{profile}.xml is not loaded.
I think logging is a common problem that all projects using Spring Boot have encountered. Am I on the right track with the above approach?
I have other solutions that work, but I find them not as elegant (conditional parsing with Janino in logback.xml file or command line property).
I found a solution and I understood why Spring doesn't use my logging.config property defined in the application.properties file.
Solution and explanation
When initializing logging, Spring Boot only looks in classpath or environment variables.
The solution I used was to include a parent logback.xml file that included the right logging config file according to the Spring profile.
logback.xml
<configuration>
<include resource="logback-${spring.profiles.active}.xml"/>
</configuration>
logback-[profile].xml (in this case, logback-dev.xml) :
<included>
<!-- put your appenders -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{ISO8601} %p %t %c{0}.%M - %m%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- put your loggers here -->
<logger name="org.springframework.web" additivity="false" level="INFO">
<appender-ref ref="CONSOLE" />
</logger>
<!-- put your root here -->
<root level="warn">
<appender-ref ref="CONSOLE" />
</root>
</included>
Note
spring.profiles.active has to be set in command line arguments when starting the app.
Example for JVM properties: -Dspring.profiles.active=dev
Reference documentation
http://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html
http://docs.spring.io/spring-boot/docs/0.5.0.M3/api/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.html
Edit (multiple active profiles)
In order to avoid multiple files, we could use conditional processing which requires Janino dependency (setup here), see conditional documentation.
With this method, we can also check for multiple active profiles at the same time. E.g (I did not test this solution, so please comment if it does not work):
<configuration>
<if condition='"${spring.profiles.active}".contains("profile1")'>
<then>
<!-- do whatever you want for profile1 -->
</then>
</if>
<if condition='"${spring.profiles.active}".contains("profile2")'>
<then>
<!-- do whatever you want for profile2 -->
</then>
</if>
<!-- common config -->
</configuration>
See #javasenior answer for another example of a conditional processing.
Another approach that could handle multiple profiles is to create a separate properties file for each environment.
application-prod.properties
logging.config=classpath:logback-prod.xml
application-dev.properties
logging.config=classpath:logback-dev.xml
application-local.properties
logging.config=classpath:logback-local.xml
BE AWARE
If you aren't careful you could end up logging somewhere unexpected
-Dspring.profiles.active=local,dev //will use logback-dev.xml
-Dspring.profiles.active=dev,local //will use logback-local.xml
Instead of adding separate logback xmls for each profile or having the IF condition , I would suggest the following (If you have less difference in the xmls') for easy conditional processing
Documentation link here:
<springProfile name="dev">
<logger name="org.sample" level="DEBUG" />
</springProfile>
<springProfile name="prod">
<logger name="org.sample" level="TRACE" />
</springProfile>
Conditional processing with logback will be a solution without many logback files. Here is a link and a sample logback configuration with spring profiles.
<configuration>
<property name="LOG_LEVEL" value="INFO"/>
<if condition='"product".equals("${spring.profiles.active}")'>
<then>
<property name="LOG_LEVEL" value="INFO"/>
</then>
<else>
<property name="LOG_LEVEL" value="ERROR"/>
</else>
</if>
.
.
appender, logger tags etc.
.
.
<root level="${LOG_LEVEL}">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
Also, you might have to add this to your pom.xml
<dependency>
<groupId>org.codehaus.janino</groupId>
<artifactId>janino</artifactId>
<version>3.0.6</version>
</dependency>
Spring has support of next tag <springProperty/> inside Logback XML file, this tag described here . It means that you can easily add variable from Spring property file, even this variable value resolves from environment/system variable by Spring.
You can specific different logback.xml for different profile, only 3 steps:
1, Specify actived profile in application.properties or application.yml:
spring.profiles.active: test
2, Config logback to include different configuration by profile:
<!DOCTYPE configuration>
<configuration scan="true" scanPeriod="30 seconds">
<springProperty scope="context" name="profile" source="spring.profiles.active"/>
<include resource="logback.${profile}.xml"/>
</configuration>
3, Create configuration file logback.test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<included>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<root level="INFO"/>
</included>
It's very simple, don't need do anything else.

How to disable console logging in spring-boot?

I'm using the default logging configuration of spring-boot.
How can I prevent the console output, while keeping the logging into a logfile configured with logging.file=myfile.log?
My goal is to not having console windows output, but only logging to that file.
Without having to create a specific logback.xml configuration. Because I'm using spring-boot for not having to configure the logging myself.
It turned out if I set the following property empty, the console logging is disabled:
logging.pattern.console=
Or commenting in xml if you use it
<!--<root level="error">-->
<!--<appender-ref ref="console"/>-->
<!--</root>-->
I created a file called logback.xml with the content:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework" level="ERROR"/>
<logger name="org.hibernate" level="ERROR"/>
</configuration>
See this link for more information: https://www.mkyong.com/spring-boot/spring-boot-test-how-to-stop-debug-logs/
Create a file logback-spring.xml in /resources/ folder of your application.
Copy the below content in logback-spring.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<configuration>
<appender name = "STDOUT" class = "ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%m%n</pattern>
</encoder>
</appender>
<root level = "INFO">
<appender-ref ref = "STDOUT"/>
</root>
</configuration>
All the supported logging systems can have the logger levels set in the Spring Environment using ‘logging.level.*=LEVEL’ where ‘LEVEL’ is one of TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF. The root logger can be configured using logging.level.root. Example application.properties:
logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=ERROR
Check this information: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-logging.html
You could also overwrite the logback configuration and provide the value OFF to it.
<configuration>
<!-- turn OFF all logging (children can override) -->
<root level="OFF">
<appender-ref ref="STDOUT" />
</root>
</configuration>
You can find more information at the following link: https://logback.qos.ch/manual/configuration.html#rootElement

Logback - dynamic configuration

Is it possible to configure logback in such way that custom logback property :
<configuration scan="true" scanPeriod="60 seconds">
<property name="logFullMessage" value="false" />
<!-- project appenders defitions -->
<!-- project loggers defititions -->
</configuration>
will influence which pattern or appender will be used ? We do have web services application which operates with large requests/responses, by default we do not wish to log request/response body, but when problem occurs we would like to have option to switch it on (logFullMessage=true) and store full response body into the log file.
To switch the used appender you could do (only relevant lines are shown):
<property name="USE_APPENDER" value="FILE1" />
<appender name="FILE1" class="ch.qos.logback.core.FileAppender">
....
</appender>
<appender name="FILE2" class="ch.qos.logback.core.FileAppender">
....
</appender>
<root level="warn">
<appender-ref ref="${USE_APPENDER}"/>
</root>
This in combination with the rescan options which you already have enabled makes it possible to switch the appenders.

Write simple logs using log4j 2

I've looked everywhere but couldn't find any guides (that made sense to me) on how to use log4j 2. As a background, I'm extremely new to log4j or any type of logging, and the closest to logging I've ever gotten was System.out.println() or PrintWriter in Java (if that's even considered logging).
I went to Apache Log4j 2 page to download the jar files, but there are 36 jar files and I have no clue which one to use.
Can anyone get me started on how to append simple String messages to a log file from my server code (in Java)? For instance, when a user connects to my server, I have a simple
System.out.println("user A connected to server at " + time);
code, which serves me no good during real usage. I'd like to replace all my System.out statements to logging using Log4j, so that I can read them later and detect any bugs/crashes in my server code.
Thanks in advance!
Is this a webapp? In other words, does it have a WEB-INF folder? If so, create a simple log4j.properties file that contains something like:
# Define the root logger with appender file
log4j.rootLogger = DEBUG, FILE
# Define the file appender
log4j.appender.FILE=org.apache.log4j.FileAppender
log4j.appender.FILE.File=log.out
# Define the layout for file appender
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.conversionPattern=%m%n
Make sure on this line:
log4j.appender.FILE.File=log.out
that you have a file actually created for it. I used log.out as an example.
Place this in your WEB-INF folder.
If it's a Maven project, find a place in your project for the file and add it manually by right clicking Run->Run Configuration -> [classpath tab] -> click on user Entries -> Advanced -> Select Add Folder -> and adding the directory where you put the file.
In your class, import:
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.FileAppender;
Then instantiate your logger object:
static Logger log = Logger.getLogger(LdapPurge.class.getName());
Try that and see if it works.
I've been working with Log4j version 2.x and recently wrote a post about its use. You can review it, maybe it will help you:
To use Log4j 2.x 2 libraries are required:
<dependency>
<groupId> org.apache.logging.log4j </groupId>
<artifactId> log4j-core </artifactId>
<version> 2.8.2 </version>
</dependency>
<dependency>
<groupId> org.apache.logging.log4j </groupId>
<artifactId> log4j-api </artifactId>
<version> 2.8.2 </version>
</dependency>
Create the log42.xml file in the resources folder:
<?xml version="1.0" encoding="UTF-8"?>
<!--https://logging.apache.org/log4j/2.x/manual/configuration.html-->
<Configuration status="TRACE" monitorInterval="30">
<!-- The properties that are needed in the configuration of the appenders are defined | property definition -->
<Properties>
<!--The pattern is defined. For writing in logs -->
<Property name="LOG_PATTERN">%d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} [%t] %-5level %logger{36} - %msg%n</Property>
<!--Storage location is defined -->
<Property name="APP_LOG_ROOT">var/log/tomcat0</Property>
</Properties>
<Appenders>
<!-- Configuration for writing events to console (cmd, ide, terminal)-->
<Console name="Console" target="SYSTEM_OUT" follow="true">
<PatternLayout pattern="${LOG_PATTERN}" />
</Console>
<!--
Configuration for writing debug events.
for more information about properties: https://logging.apache.org/log4j/2.x/manual/appenders.html
-->
<RollingFile name="debugLog"
fileName="${APP_LOG_ROOT}/logs/automatizador-debug.log"
filePattern="${APP_LOG_ROOT}/logs/$${date:yyyy-MM}/automatizador-debug-%d{yyyy-MM-dd}-%i.log.gz">
<LevelRangeFilter minLevel="DEBUG" maxLevel="DEBUG" onMatch="ACCEPT"
onMismatch="DENY"/>
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="19500KB" />
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingFile>
<!--Define the logger-->
<Loggers>
<!--only log events from type "debug" for the package: com.advanced.development -->
<Logger name="com.advanced.development" additivity="false"
level="trace">
<AppenderRef ref="debugLog" />
<AppenderRef ref="Console" />
</Logger>
<Root level="error">
<AppenderRef ref="debugLog"/>
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
For more details: https://su9.co/9BaE74E

Categories

Resources