Currently I have logback configuration file i.e logback.xml
which is src/main/resources
. I want to set the logging level but i want control outside of
I've managed to put an external configuration file with tomcat/Spring logback
Context file of tomcat :
<?xml version='1.0' encoding='utf-8'?>
<Context path="/appName">
<Environment name="pathCfg" value="${user.home}/applications/appName/cfg" type="java.lang.String" override="false" />
</Context>
web.xml :
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/web-application-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Listener for external logback configuration file -->
<listener>
<listener-class>com.package.client.servlet.LogbackConfiguratorContextListener</listener-class>
</listener>
web-application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
<!-- Configuration -->
<jee:jndi-lookup id="configurationPath" jndi-name="pathCfg"></jee:jndi-lookup>
<bean id="configurationPathResource" class="org.springframework.core.io.FileSystemResource">
<constructor-arg ref="configurationPath" />
</bean>
<util:properties id="environnementProperties" location="file:${java:comp/env/pathCfg}/environnement.properties" />
</beans>
the logback configurator class:
package com.package.client.servlet;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.web.context.support.WebApplicationContextUtils;
import ch.qos.logback.classic.BasicConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.util.ContextInitializer;
import ch.qos.logback.core.joran.spi.JoranException;
/**
* Servlet context listener in order to use an external configuration file for logback.
*
*/
public class LogbackConfiguratorContextListener implements ServletContextListener
{
private static final Logger LOGGER = LoggerFactory.getLogger(LogbackConfiguratorContextListener.class);
/**
* Logback configuration file uri.
*/
@Value("#{environnementProperties['logback.configuration.file.uri']}")
private String logbackConfigurationUri;
/**
* Configuration path resource for configuration files.
*/
@Value("#{configurationPathResource}")
private Resource configurationPathResource;
/**
* initialize logback with external configuration file.
*/
@Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
autowireThis(servletContextEvent.getServletContext());
Resource logbackConfigurationResource = null;
if (StringUtils.isNotBlank(logbackConfigurationUri))
{
if (StringUtils.containsAny(logbackConfigurationUri, new char[] { '/', '\\' }))
{
logbackConfigurationResource = new FileSystemResource(logbackConfigurationUri);
}
else if (configurationPathResource != null)
{
try
{
logbackConfigurationResource = new FileSystemResource(new File(configurationPathResource.getFile(),
logbackConfigurationUri));
}
catch (IOException exception)
{
LOGGER.error("Can't read resource " + configurationPathResource.getFilename(), exception);
}
}
}
if (logbackConfigurationResource != null)
{
if (logbackConfigurationResource.exists())
{
LOGGER.info("Found logback configuration " + logbackConfigurationResource.getDescription()
+ " - Overriding default configuration");
JoranConfigurator configurator = new JoranConfigurator();
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
configurator.setContext(loggerContext);
try
{
configurator.doConfigure(logbackConfigurationResource.getFile());
LOGGER.info("default configuration overrided by logback configuration "
+ logbackConfigurationResource.getDescription());
}
catch (Exception exception)
{
try
{
new ContextInitializer(loggerContext).autoConfig();
}
catch (JoranException e)
{
BasicConfigurator.configureDefaultContext();
LOGGER.error("Can't configure default configuration", exception);
}
LOGGER.error(
"Can't configure logback with specified " + logbackConfigurationResource.getDescription()
+ " - Keep default configuration", exception);
}
}
else
{
LOGGER.error("Can't read logback configuration specified file at "
+ logbackConfigurationResource.getDescription() + " - Keep default configuration");
}
}
else
{
LOGGER.info("No logback configuration file specified - Keep default configuration");
}
}
/**
* Autowire this bean by spring
* @param servletContext le contexte de la servlet
*/
private void autowireThis(ServletContext servletContext)
{
WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getAutowireCapableBeanFactory()
.autowireBean(this);
}
@Override
public void contextDestroyed(ServletContextEvent sce)
{
}
}
and finally the environnement.properties
# Configuration file
logback.configuration.file.uri=C:/Users/olejacques/applications/appName/cfg/log.xml
I suggest you use 2 logback configuration files. The first one in the classpath defined as:
<configuration scan="true">
<property file="/otherfolder/project.properties" />
<include file="/otherfolder/logback.xml"/>
</configuration>
and you put all the configuration in the other one. Logback will scan both files for updates.
One way that it worked for us was to externalize the configuration file like below. Ignore the spring profiles part if that doesn't apply to you. So as soon as you update the configuration in logback-include.xml
, let's say by changing root level to info, the application picks it up within 30 seconds (configurable).
Reference: https://logback.qos.ch/manual/configuration.html#fileInclusion
web.xml
We used spring profiles, but it works even without it.
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
<listener>
<listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class>
</listener>
<context-param>
<param-name>logbackConfigLocation</param-name>
<param-value>classpath:properties/${spring.profiles.active}/logback.xml</param-value> <!-- with profiles, config file in classpath includes ext. file -->
<!-- <param-value>file:logback-ext-config-path/logback.xml</param-value> --> <!-- directly using external config -->
<!-- <param-value>classpath:logback.xml</param-value> --> <!-- without profiles -->
</context-param>
logback.xml (in the classpath)
Note the debug="true"
which will tell you when the configuration is updated.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration debug="true" scan="true" scanPeriod="30 seconds">
<include file="${logback-ext-config-include-file-path}/logback-include.xml"/>
</configuration>
logback-include.xml (external location)
Note the use of <included>
instead of configuration. Don't miss this part!
<included>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${logfile-path}/log-file.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- daily rollover -->
<fileNamePattern>logFile.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- keep 7 days' worth of history -->
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%date [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="FILE"/>
</root>
</included>
External properties is one way of controlling logging level externally. Logback's file inclusion feature is another. By the way, the auto-reload feature works for included configuration files but not properties files.
If you just want to change the log level you don't need to override the whole file, you can do this:
<root level="${log.level:-INFO}">
Then if you set the system property:
-Dlog.level=DEBUG
It will override the default of 'INFO'.