03
Jan
10

Using JSF to change Log4j Log Levels at Runtime


This page will show you how you can easily code a JSF form to inspect the current log4j logging levels and allow the user to change them at runtime. The changes take effect immediately. The complete implementation should take about 45 minutes.

Requirements

Configuration

Just add the following to the pom.xml file.

    <dependency>
    	<groupId>commons-logging</groupId>
    	<artifactId>commons-logging</artifactId>
    	<version>1.1</version>
    </dependency>
    <dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.15</version>
	<exclusions>
		<exclusion>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jdmk</groupId>
			<artifactId>jmxtools</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jmx</groupId>
			<artifactId>jmxri</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
		</exclusion>
	</exclusions>
</dependency>
	<dependency>
		<groupId>org.apache.myfaces.tomahawk</groupId>
		<artifactId>tomahawk</artifactId>
		<version>1.1.9</version>
	</dependency>

and regenerate the eclipse / idea project.

mvn eclipse:clean eclipse:eclipse

Refresh the eclipse project.

Put the following file into the

src/main/resources/log4j.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<appender name="A1" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n" />
		</layout>
	</appender>

	<root>
		<priority value="fatal" />
		<appender-ref ref="A1" />
	</root>
	<logger name="org.vtechfw">
		<level value="debug" />
		<appender-ref ref="A1" />
	</logger>
	<logger name="org.vtechfw.utils">
		<level value="trace" />
		<appender-ref ref="A1" />
	</logger>
</log4j:configuration>

Managed Logger

This is just a wrapper bean to help configure the loggers.

src/main/java/org/vtechfw/utils/logging/ManagedLogger.java

package org.vtechfw.utils.logging;

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

/**
 * Wrapper to the Log4j Logger to help configure the logging levels.
 */
public class ManagedLogger {
	private Logger logger;

	
	public ManagedLogger() {
		super();
	}

	public ManagedLogger(Logger logger) {
		super();
		this.logger = logger;
	}

	public void setLogger(Logger logger) {
		this.logger = logger;
	}

	public final String getName() {
		return logger.getName();
	}

	public Logger getLogger() {
		return logger;
	}
	public Level getLevel() {
		return logger.getEffectiveLevel();
	}
	public void setLevel(Level level) {
		if(level!=null) {			
			logger.setLevel(level);
		}
	}
}

Managed Bean

Create the following class in the src/main/java/org/vtechfw/utils/logging folder

Log4jManager.java

package org.vtechfw.utils.logging;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;

import javax.faces.component.html.HtmlDataTable;

import org.apache.log4j.Level;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

public class Log4jManager {
	// used for regular logging.
	protected Logger logger = Logger.getLogger(getClass());
	private Logger currentLogger;
	private String logLevel;
	private HtmlDataTable htmlDataTable;

	@SuppressWarnings("unchecked")
	public List<Logger> getLoggerList() {
		Enumeration<Logger> enumeration = LogManager.getCurrentLoggers();
		List<Logger> list = Collections.<Logger>list(enumeration);
		list.add(LogManager.getRootLogger());

		Collections.sort(list, new Comparator<Logger>() {

			@Override
			public int compare(Logger o1, Logger o2) {
				String o1Name = o1.getName();
				String o2Name = o2.getName();

				return o1Name.compareTo(o2Name);
			}
		});

		return list;
	}

	public List<ManagedLogger> getManagedLoggerList() {
		List<Logger> list = getLoggerList();
		List<ManagedLogger> managedLoggerList = new ArrayList<ManagedLogger>();
		for (Logger logger : list) {
			ManagedLogger e = new ManagedLogger(logger);
			managedLoggerList.add(e);
		}
		return managedLoggerList;
	}

	public Logger getRootLogger() {
		return LogManager.getRootLogger();
	}

	public void setCurrentLogger(Logger currentLogger) {
		this.currentLogger = currentLogger;
	}

	public Logger getCurrentLogger() {
		return currentLogger;
	}

	public void setHtmlDataTable(HtmlDataTable htmlDataTable) {
		this.htmlDataTable = htmlDataTable;
	}

	public HtmlDataTable getHtmlDataTable() {
		return htmlDataTable;
	}

	public String setLoggerLevel() {

		// find out the currently selected logger.
		currentLogger = (Logger)htmlDataTable.getRowData();
		Level level = Level.toLevel(logLevel);
		currentLogger.setLevel(level);
		return "refresh";
	}

	public void setLogLevel(String logLevel) {
		this.logLevel = logLevel;
	}

	public String getLogLevel() {
		return logLevel;
	}
}

Converter

The following class converts between the text representation of the Logger and the actual logger instance.

src/main/java/org/vtechfw/utils/logging/LevelConverter.java

package org.vtechfw.utils.logging;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;

import org.apache.log4j.Level;

public class LevelConverter implements Converter {

	@Override
	public Object getAsObject(FacesContext context, UIComponent component,
			String value) throws ConverterException {
		Level level = null;

		if(value==null || "".equals(value)) {
			level = null;
		} else {
			level = Level.toLevel(value);
		}
		return level;
	}

	@Override
	public String getAsString(FacesContext context, UIComponent component,
			Object value) throws ConverterException {
		if(value instanceof String) {
			return (String)value;
		}
		if(value instanceof Level) {
			Level level = (Level) value;
			return level.toString();
		}
		return "parent";
	}

}

JSP file

This is where all the magic happens. Its a short file that hopefully is self explanatory.

src/main/webapp/log4jManager.jsp

<%@taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%><%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="f"  uri="http://java.sun.com/jsf/core"%>
<%@ taglib prefix="h"  uri="http://java.sun.com/jsf/html"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Log4j Management Console</title>
</head>
<body>
<f:view>
	<h:form id="levelconfigform">
	<h3>Log Level Configuration Util (Log4j)</h3>
		<t:dataTable border="1" value="#{log4jManager.managedLoggerList}" var="logger" binding="#{log4jManager.htmlDataTable}" sortable="true">
			<h:column id="column1">
				<f:facet name="header">
					<h:outputText value="Name"></h:outputText>
				</f:facet>
				<h:outputText value="#{logger.name}"></h:outputText>
			</h:column>
			<h:column id="column3">
				<f:facet name="header">
					<h:outputText value="Level"></h:outputText>
				</f:facet>
				<h:selectOneMenu id="logLevel" value="#{logger.level}" onchange="submit();">
					<f:selectItem itemLabel="ALL" itemValue="ALL"/>
					<f:selectItem itemLabel="TRACE" itemValue="TRACE"/>
					<f:selectItem itemLabel="DEBUG" itemValue="DEBUG"/>
					<f:selectItem itemLabel="INFO" itemValue="INFO"/>
					<f:selectItem itemLabel="WARN" itemValue="WARN"/>
					<f:selectItem itemLabel="ERROR" itemValue="ERROR"/>
					<f:selectItem itemLabel="FATAL" itemValue="FATAL"/>
					<f:selectItem itemLabel="OFF" itemValue="OFF"/>
				</h:selectOneMenu>
			</h:column>
		</t:dataTable>
	</h:form>
</f:view>
</body>
</html>

Faces Config File

I have a lot of junk in mine so I am not going to include the whole thing. Here are the important sections.

src/main/webapp/WEB-INF/faces-config.xml

	<converter>
		<display-name>Level Converter</display-name>
		<converter-for-class>org.apache.log4j.Level</converter-for-class>
		<converter-class>org.vtechfw.utils.logging.LevelConverter</converter-class>
	</converter>

	<managed-bean>
		<managed-bean-name>log4jManager</managed-bean-name>
		<managed-bean-class>org.vtechfw.utils.logging.Log4jManager</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
	</managed-bean>

	<navigation-rule>
	        <display-name>log4jManager</display-name>
		<from-view-id>/log4jManager.jsp</from-view-id>
		<navigation-case>
			<from-outcome>refresh</from-outcome>
			<to-view-id>/log4jManager.jsp</to-view-id>
		</navigation-case>
	</navigation-rule>

Running the project

Drop to the command line and type in the following:

mvn jetty:run

Using your browser navigate to the following URL:

http://localhost:8080/log4jManager.jsf

You will be presented with a table of loggers and their associated level. The table is sortable using the table headings. Using the drop down you can change the log level of each logger. The changes take effect immediately.

The only bug with this jsf component is that when you select a logging level using the drop down the logging level of the item adjacent to it changes. But it starts working as expected with the second attempt. Having figured out what the problem is. If you guys figure it out let me know by commenting below.

That’s all for now!

Advertisements

2 Responses to “Using JSF to change Log4j Log Levels at Runtime”


  1. 1 Ruben
    November 19, 2010 at 8:37 am

    Thanks this is just wanted I needed for my project.

  2. 2 Folger
    April 17, 2013 at 9:57 am

    does not work for me, the selectOneMenu does not get selected with the right value.


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 75 other followers

January 2010
S M T W T F S
« Dec   Feb »
 12
3456789
10111213141516
17181920212223
24252627282930
31  

Blog Stats

  • 813,810 hits

%d bloggers like this: