How do I change my Log4J settings while my web application is running?
Author: Deron Eriksson
Description: This Java tutorial describes how to change Log4J settings in a web application at runtime.
Tutorial created using: Windows XP || JDK 1.5.0_09 || Eclipse Web Tools Platform 1.5.1 || Tomcat 5.5.20


In another tutorial, we created a log4j web application demonstration project with the following structure:

'log4j-webapp-demo' project

In this tutorial I'll take that project and make some modifications to the Log4JInitServlet so that we can make runtime changes to our log4j settings. This can be a useful thing. As an example, if your web application in your production environment is having problems and your log level is set to ERROR, you can lower it to DEBUG temporarily to help you diagnose the problems that you're having.

Let's look at the web.xmlW file for this project. This web.xml file is identical to the web.xml file in the other tutorial except that I've added an /init mapping to the Log4JInitServlet so that we can hit the init servletW via a browser.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="log4j-webapp-demo" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<servlet>
		<servlet-name>Log4JTestServlet</servlet-name>
		<servlet-class>test.Log4JTestServlet</servlet-class>
	</servlet>
	<servlet>
		<servlet-name>Log4JInitServlet</servlet-name>
		<servlet-class>test.Log4JInitServlet</servlet-class>
		<init-param>
			<param-name>log4j-properties-location</param-name>
			<param-value>WEB-INF/log4j.properties</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>Log4JTestServlet</servlet-name>
		<url-pattern>/test</url-pattern>
	</servlet-mapping>
	<servlet-mapping>
		<servlet-name>Log4JInitServlet</servlet-name>
		<url-pattern>/init</url-pattern>
	</servlet-mapping>
</web-app>

The Log4JInitServlet's init method is identical to the init method in the previous tutorial. However, I've added a doGet() method to handle browser requests so that we can hit the init servlet via a browser. The goGet() method looks for two possible parameters, 'logLevel' or 'reloadPropertiesFile'. If the 'logLevel' parameter is present, it's value is used to set the log4j log level. If the 'reloadPropertiesFile' parameter is present the log4j.properties file will be reloaded so that changes to the file will be reflected in log4j.

The setLogLevelWithParameter() method utilized the logLevel parameter to change the log4j logging level, and the loadLog4jPropertiesFile() method reloads the log4j.properties file so that new changes to the file get reflected in the web application's logging.

Log4JInitServlet.java

package test;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class Log4JInitServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	public void init(ServletConfig config) throws ServletException {
		System.out.println("Log4JInitServlet is initializing log4j");
		String log4jLocation = config.getInitParameter("log4j-properties-location");

		ServletContext sc = config.getServletContext();

		if (log4jLocation == null) {
			System.err.println("*** No log4j-properties-location init param, so initializing log4j with BasicConfigurator");
			BasicConfigurator.configure();
		} else {
			String webAppPath = sc.getRealPath("/");
			String log4jProp = webAppPath + log4jLocation;
			File yoMamaYesThisSaysYoMama = new File(log4jProp);
			if (yoMamaYesThisSaysYoMama.exists()) {
				System.out.println("Initializing log4j with: " + log4jProp);
				PropertyConfigurator.configure(log4jProp);
			} else {
				System.err.println("*** " + log4jProp + " file not found, so initializing log4j with BasicConfigurator");
				BasicConfigurator.configure();
			}
		}
		super.init(config);
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("This is the Log4JInitServlet<br/>");
		String logLevel = request.getParameter("logLevel");
		String reloadPropertiesFile = request.getParameter("reloadPropertiesFile");
		if (logLevel != null) {
			setLogLevelWithParameter(out, logLevel);
		} else if (reloadPropertiesFile != null) {
			out.println("Attempting to reload log4j properties file<br/>");
			loadLog4jPropertiesFile(out);
		} else {
			out.println("no logLevel or reloadPropertiesFile parameters were found<br/>");
		}
	}

	private void setLogLevelWithParameter(PrintWriter out, String logLevel) {
		Logger root = Logger.getRootLogger();
		boolean logLevelRecognized = true;
		if ("DEBUG".equalsIgnoreCase(logLevel)) {
			root.setLevel(Level.DEBUG);
		} else if ("INFO".equalsIgnoreCase(logLevel)) {
			root.setLevel(Level.INFO);
		} else if ("WARN".equalsIgnoreCase(logLevel)) {
			root.setLevel(Level.WARN);
		} else if ("ERROR".equalsIgnoreCase(logLevel)) {
			root.setLevel(Level.ERROR);
		} else if ("FATAL".equalsIgnoreCase(logLevel)) {
			root.setLevel(Level.FATAL);
		} else {
			logLevelRecognized = false;
		}

		if (logLevelRecognized) {
			out.println("Log level has been set to: " + logLevel + "<br/>");
		} else {
			out.println("logLevel parameter '" + logLevel + "' level not recognized<br/>");
		}
	}

	private void loadLog4jPropertiesFile(PrintWriter out) {
		ServletContext sc = getServletContext();
		String log4jLocation = getInitParameter("log4j-properties-location");

		if (log4jLocation == null) {
			out.println("*** No log4j-properties-location init param, so initializing log4j with BasicConfigurator<br/>");
			BasicConfigurator.configure();
		} else {
			String webAppPath = sc.getRealPath("/");
			String log4jProp = webAppPath + log4jLocation;
			File log4jFile = new File(log4jProp);
			if (log4jFile.exists()) {
				out.println("Initializing log4j with: " + log4jProp + "<br/>");
				PropertyConfigurator.configure(log4jProp);
			} else {
				out.println("*** " + log4jProp + " file not found, so initializing log4j with BasicConfigurator<br/>");
				BasicConfigurator.configure();
			}
		}
	}

}

Here is the log4j.properties file.

log4j.properties

# This sets the global logging level and specifies the appenders
log4j.rootLogger=INFO, myConsoleAppender

# settings for the console appender
log4j.appender.myConsoleAppender=org.apache.log4j.ConsoleAppender
log4j.appender.myConsoleAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.myConsoleAppender.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Our test servlet is shown below. It contains a logger called log. Within its doGet() method, it displays the message 'Howdy' in response to a browser request, and it contains calls to log.debug(), log.info(), log.warn(), log.error(), and log.fatal().

Log4JTestServlet.java

package test;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

public class Log4JTestServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {

	private static final long serialVersionUID = 1L;
	static Logger log = Logger.getLogger(Log4JTestServlet.class);

	public Log4JTestServlet() {
		super();
	}

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html");
		PrintWriter out = response.getWriter();
		out.println("Howdy<br/>");
		log.debug("debug message");
		log.info("info message");
		log.warn("warn message");
		log.error("error message");
		log.fatal("fatal message");
	}

}

Let's start up the web application. The console output is shown below.

Console output

Let's hit the test servlet.

Hitting test servlet in web browser

As you can see, 4 log messages are output to the console by the test servlet. Notice that the log.debug() message isn't displayed, since the log4j.properties log level is set to INFO.

Console output - DEBUG level isn't displayed

Now let's hit the init servlet and change the log4l log level via the logLevel parameter. Let's set the log level to ERROR, so let's pass in the parameter 'logLevel=ERROR'.

Changing log level to ERROR via init servlet

Let's hit the test servlet again.

Hitting test servlet in web browser again

As you can see in the console output, two new log4j messages were displayed. These were the error and fatal messages, since we used the init servlet to set the log level to ERROR.

Console output - DEBUG, INFO, and WARN not displayed in last output

Next, let's modify our log4j.properties file so that the logging level is set to DEBUG.

Changing log level in log4j.properties to DEBUG

Now, I'll hit the log4j init servlet with the reloadPropertiesFile parameter. This will reload the log4j.properties file settings into the web application. Note that we've changed the file's log level from INFO to DEBUG.

Hitting init servlet with reloadPropertiesFile parameter

Let's hit the test servlet one more time.

Hitting test servlet in web browser again

In our console window, we can see that five new log4j messages appeared. This includes the debug message since we changed the log4j logging level to DEBUG.

Console output shows that log4j log level has been set to DEBUG

In this tutorial, we've seen two ways that we can use a servlet to update our log4j settings while our application is running. One technique is to get the root logger via Logger.getLogger() and call methods on that, such as setLevel(). Another technique is to make changes to the log4j.properties file, and then to reload those changes into the application. The nice thing about the second technique is that the settings will still be there if we restart our servlet container (ie, Tomcat). As you can imagine, we could continue expanding on these principles and do something like store log4j properties in a databaseW and use a password-protected servlet to update these settings.