Posts Tagged ‘log4j

14
Feb
16

Log4j configuration with Java CDI (Contexts and Dependency Injection)

This page describes how to configure log4j in a Java CDI stand-alone application.

All of us seen code that looks like this over and over again.

private Logger logger = Logger.getLogger(this.getClass().getName());

We will use Java Contexts and Dependency Injection to create a custom log4j Logger for a service class.

Modify the pom.xml

vi pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>log4jCDI</artifactId>
	<version>20160214</version>

	<pluginRepositories>
		<pluginRepository> <!-- Ignore this repository. Its only used for document publication. -->
			<id>numberformat-releases</id>
			<url>https://raw.githubusercontent.com/numberformat/wordpress/master/20130213/repo/</url>
		</pluginRepository>
	</pluginRepositories>
	
	<dependencies>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.jboss.weld.se</groupId>
			<artifactId>weld-se</artifactId>
			<version>2.2.6.Final</version>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.2</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		
			<plugin> <!-- Ignore this plugin. Its only used for document publication. -->
				<groupId>github.numberformat</groupId>
				<artifactId>blog-plugin</artifactId>
				<version>1.0-SNAPSHOT</version>
				<configuration>
					<gitUrl>https://github.com/numberformat/wordpress/tree/master/${project.version}/${project.artifactId}</gitUrl>
				</configuration>
				<executions>
					<execution>
						<id>1</id>
						<phase>site</phase>
						<goals>
							<goal>generate</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>

Setup the log4j configuration files. We will be using a very basic file that outputs to the console.
vi 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="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="com.test" additivity="true">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

Producer (Factory)

The following CDI Producer creates a log4j Logger whenever it encounters a @Inject. Java keeps track of all the methods that are annotated with @Producer. If it comes across an inject
that requires a type produced by one of these producers then it calls the producer method to get this instance. The producer method here accepts a InjectionPoint metadata object that helps identify the location of where the injection is occuring. We will use this information to create a log4j logger custom to the class where its being injected into.

vi src/main/java/com/test/LoggerProducer.java

package com.test;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;

import org.apache.log4j.Logger;

public class LoggerProducer {
	@Produces
	public Logger produceLogger(InjectionPoint injectionPoint) {
		return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
	}
}

Service Class

The following service class is responsible for printing out the different levels of log messages.

vi src/main/java/com/test/TestLogger.java

package com.test;

import javax.inject.Inject;

import org.apache.log4j.Logger;

public class TestLogger {	
	@Inject
	private transient Logger logger;
	
	private static final String THIS_IS = "Method: ";
	
	public void writeLogAllLevels() {
		logger.fatal(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.error(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.warn(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.info(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
		logger.debug(THIS_IS + new Object(){}.getClass().getEnclosingMethod().getName());
	}
}

Junit Test Case

Finally test the logging with a simple junit test case.

vi src/test/java/log4jCDI/TestApp.java

package log4jCDI;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.junit.Test;

import com.test.TestLogger;

public class TestApp {
	static WeldContainer weld=null;
	static {
        weld = new Weld().initialize();
	}
	@Test
	public void test() {
        TestLogger testLogger = weld.instance().select(TestLogger.class).get();
        testLogger.writeLogAllLevels();
	}

}

Log4j xml file

This bare bones log4j file will print all log levels to the console.

vi 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="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n"/>
        </layout>
    </appender>
    <logger name="com.test" additivity="true">
        <level value="DEBUG"/>
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

beans.xml

Finally create a blank beans.xml file in your src/main/java/META-INF folder.

Run the Junit Test Case

At this time you will see output of each of the messages to the console similar to this:

2016-02-14 15:58:18,840 FATAL com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 ERROR com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 WARN  com.test.TestLogger - Method: writeLogAllLevels
2016-02-14 15:58:18,841 INFO  com.test.TestLogger - Method: writeLogAllLevels
Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.
16
Feb
13

Log4j2 Configuration with Multiple Web Apps

This page describes how to use the new Log4j 2 Framework within multiple web applications deployed to a single web container with a shared log4j jar.

Full downloadable source for this page is available here. Corrections and enhancements are welcome, fork, change and push back to GitHub.

Background

Historically Java Web Application WAR files have included their own copy of the log4j jar files. This allowed multiple applications to co exist and use their own log4j configuration. However as web applications get smaller and focused smaller tasks (possibly RESTful applications) you will find yourself deploying multiple WAR files to the same web container. It doesnt make too much sense to have each WAR contain its own copy of log4j.jar.

Ideally it would be nice to have one log4j.jar file at the container level that each application can share. However, its not as simple as moving it out of the WAR file because of the way log4j configures itself.

This page covers a method described in the log4j 2 homepage.

Place the logging jars in the container’s classpath and use the default ClassLoaderContextSelector. Include the Log4jContextListener in each web application. Each ContextListener can be configured to share the same configuration used at the container or they can be individually configured. If status logging is set to debug in the configuration there will be output from when logging is initialized in the container and then again in each web application.

Requirements

  • Java 5 or later
  • Maven 2 or later
  • Log4j 2
  • Apache Tomcat 6

Procedure

We will create 2 apps and deploy them on Tomcat and verify that they are both logging to the console as well as individual files specified in their configs.

Any one who worked with log4j 1.x knows that this was a pain to setup with multiple web applications. Log4j 2 makes it a lot easier.

  • web-log-test – outputs to SYSOUT and ${user.home}/APPBASE/logs/web-log-test.log
  • web-log-test2 – outputs to SYSOUT and ${user.home}/APPBASE/logs/web-log-test2.log

Project Configuration

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>web-log-test</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

	<pluginRepositories>
	  <pluginRepository>
	    <id>numberformat-releases</id>
	    <url>https://raw.github.com/numberformat/20130213/master/repo</url>
	  </pluginRepository>
	</pluginRepositories>

  <dependencies>
  	<dependency>
  		<groupId>javax.servlet</groupId>
  		<artifactId>servlet-api</artifactId>
  		<version>2.5</version>
  		<scope>provided</scope>
  	</dependency>
  	<dependency>
  		<groupId>org.apache.logging.log4j</groupId>
  		<artifactId>log4j-core</artifactId>
  		<version>2.0-beta4</version>
  		<scope>provided</scope>
  	</dependency>
  	<dependency>
  		<groupId>org.apache.logging.log4j</groupId>
  		<artifactId>log4j-api</artifactId>
  		<version>2.0-beta4</version>
  		<scope>provided</scope>
  	</dependency>
  	<dependency>
  		<groupId>org.apache.logging.log4j.adapters</groupId>
  		<artifactId>log4j-web</artifactId>
  		<version>2.0-beta4</version>
  		<scope>provided</scope>
  	</dependency>
  	<dependency>
  		<groupId>org.apache.logging.log4j.adapters</groupId>
  		<artifactId>log4j-1.2-api</artifactId>
  		<version>2.0-beta4</version>
  		<scope>provided</scope>
  	</dependency>
  </dependencies>

	<build>
		<finalName>web-log-test</finalName>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.0.2</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
	 		<plugin>
	 			<groupId>github.numberformat</groupId>
	 			<artifactId>blog-plugin</artifactId>
	 			<version>1.0-SNAPSHOT</version>
	 			<configuration>
				<gitUrl>https://github.com/numberformat/20130216</gitUrl>
	 			</configuration>
	        <executions>
	          <execution>
	            <id>1</id>
	            <phase>site</phase>
	            <goals>
	              <goal>generate</goal>
	            </goals>
	          </execution>
	        </executions>
	 		</plugin>
		</plugins>
	</build>
</project>

Web Configuration

The following sets up the log4jContextListener by specifying the log4jConfiguration parameter pointing it to the location of your config.

You may specify a file in the classpath or an absolute location on your system. If you want to keep this file in your WEB-INF folder then you can extend the class and customize it to suit your requirement.

vi src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="WebApp_ID" version="2.5">
	<display-name>Sample Web Application</display-name>

	<context-param>
	   <param-name>log4jConfiguration</param-name>
	   <param-value>log4j2.xml</param-value>
	</context-param>

	<listener>
		<listener-class>org.apache.logging.log4j.core.web.Log4jContextListener</listener-class>
	</listener>

</web-app>

Log4j Configuration

In log4j 2 the name of the config file should be log4j2.xml. For this page our configuration file is very simple. See the log4j 2 site for further details about other configuration options.

vi src/main/resources/log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration strict="true">
<!-- include this to enable log4j internal debug messages: status="debug" -->
<appenders>
	<appender type="Console" name="STDOUT">
		<layout type="PatternLayout" pattern="%d %-5p %c - %m%n"/>
	</appender>
	<appender type="File" name="File" fileName="${sys:user.home}/APPBASE/logs/web-log-test.log">
		<layout type="PatternLayout">
			<pattern>%d %-5p %c - %m%n</pattern>
		</layout>
	</appender>
</appenders>

<loggers>
	<logger name="org.apache.jsp" level="debug">
		<appender-ref ref="File"/>
	</logger>
	<root level="trace">
		<appender-ref ref="STDOUT"/>
	</root>
</loggers>

</configuration>

Test Page

We can test the application by using a simple jsp.

vi src/main/webapp/snoop.jsp

<HTML>
<HEAD>
	<TITLE>JSP snoop page</TITLE>
	<%@ page import="org.apache.log4j.*" %>
</HEAD>
<BODY>

<H1>WebApp JSP Log4j Test</H1>

<h3>Apache Log4j Logging</h3>

<%
Logger logger = Logger.getLogger(getClass());
if("submit".equals((String)request.getParameter("log"))) {
	String text = (String)request.getParameter("text");
	String level = (String)request.getParameter("level");
	logger.log(Level.toLevel(level), text);
}
%>

<form action="" method="GET">
<TABLE border="1">
<TR valign=top>
	<TH align=left>Level</TH>
	<TH align=left>Test Message</TH>
</TR>
<TR valign=top>
	<TD>
		<select name="level">
			<option value="ERROR">ERROR</option>
			<option value="WARN">WARN</option>
			<option value="INFO" selected="selected">INFO</option>
			<option value="DEBUG">DEBUG</option>
			<option value="TRACE">TRACE</option>
		</select>
	</TD>
	<TD>
		<input name="text" type="text" size="35" value="<%=new java.util.Date()%>">
		<input name="log" type="submit" value="submit">
	</TD>
</TR>
</TABLE>
</form>

</BODY>
</HTML>

Finally copy the following files to the “tomcat6/lib” folder. You may obtain these files from the .m2 folder after the project is built. Or you can get them from the apache download site. (at the time of this writing only beta4 was the latest available)

  • log4j-core-2.0.jar – core libarary.
  • log4j-api-2.0.jar – API Shell Classes to be used by your code
  • log4j-web-2.0.jar – Servlet Listener to initialize Logging
  • log4j-1.2-api-2.0.jar – log4j 1.x support for legacy code that still uses it.

Build and Run the Application

perform the build by typing:

mvn clean compile package

You will notice that the WAR file does not contain any log4j jars.

Copy the war file into your tomcat6/webapps folder.

navigate to: http://localhost:8080/web-log-test/snoop.jsp

Modify and Deploy the Second Application

For the second application we want to modify the log4j2.xml to write the log to ${user.home}/APPBASE/logs/web-log-test2.log

Rename the WAR file to web-log-test2.war deploy to the same server.

Restart and test.

Using a new TAB, navigate to: http://localhost:8080/web-log-test2/snoop.jsp

Verify that the logs are being written the console and to each file independently.

Appendix

log4j 1.x configuration available here

Full downloadable source for this page is available here.
14
Jan
12

Default Log4j Property File

This page describes the process of setting up a Log4j configuration file. It goes thru some example log4j configuration files and describes what each one does. I finish up by discussing TimeBasedRollingPolicy with an example.

Requirements

  • Basic Understanding of Maven

POM

The following dependencies should be placed in your pom.xml file before starting.

<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>commons-logging</groupId>
	  <artifactId>commons-logging</artifactId>
	  <version>1.0.4</version>
	</dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>

Programmatic Configuration

Before we discuss the proper way to initialize logging lets talk about a quick and dirty way. Put the following static block of code into your entry point (Main) class. It sets the logging level of the default appender to debug and sets it to output to the console.

static {
	Logger root = Logger.getRootLogger();
	root.addAppender(new ConsoleAppender(
	    new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));		
}

Property Based Configuration (Legacy)

This is the default log4j.properties file with basic logging to the console. Be careful with it because it will be verbose if you are running in a servlet engine or JEE container. a more reasonable value for the rootLogger would be FATAL. Additional packages can be defined on a case by case basis.

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n

Additional packages could be added to to the file by putting in the following lines:

log4j.logger.org.vtechfw=DEBUG
log4j.logger.org.vtechfw.web=INFO

XML Based Configuration

A similar configuration however in XML is seen below.

Its important to understand the difference between the logger level and the appender threshold.

Once the logger level test is passed the log message will be sent to the appender associated with the logger as well as the appenders associated with the parent loggers without the log-level being re-tested. This is counter intuitive to the expected outcome.

It is possible to override the default behavior so that appender accumulation is no longer additive by setting the additivity flag to false.

<!DOCTYPE log4j:configuration PUBLIC
  "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
  
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
        <param name="Target" value="System.out"/>
        <param name="Threshold" value="INFO" />
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %-5p [%c] %m%n"/>
        </layout>
    </appender>
    <logger name="org.springframework" additivity="false">
        <level value="INFO"/>
        <appender-ref ref="CONSOLE"/> 
    </logger>
    <root>
        <level value="ERROR"/>
        <appender-ref ref="CONSOLE"/>
    </root> 
</log4j:configuration>

RollingFileAppender Example

The following appender will roll when the file size is reached.

<!DOCTYPE log4j:configuration PUBLIC
  "-//APACHE//DTD LOG4J 1.2//EN" "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/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{HH:mm:ss,SSS} %-5p [%c] %m%n"/>
		</layout>
	</appender>

	<appender name="R" class="org.apache.log4j.RollingFileAppender">
		<param name="file" value="/tmp/service.log" />
		<param name="MaxFileSize" value="100KB" />
		<!-- Keep one backup file -->
		<param name="MaxBackupIndex" value="1" />
		<layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %-5p [%c] %m%n"/>
		</layout>
	</appender>
	<logger name="com.test">
		<level value="DEBUG"></level>
		<appender-ref ref="R"></appender-ref>
	</logger>
	<root>
		<priority value="DEBUG"></priority>
		<appender-ref ref="A1"></appender-ref>
	</root>
</log4j:configuration>

DailyRollingFileAppender Example

The following appender will log to the /tmp/test.log file and at midnight it will roll it over to a dated filename and continue with a fresh test.log file for the next day.

	<appender name="DRFA" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="File" value="/tmp/test.log"/>
		<param name="DatePattern" value="'.'yyyy-MM-dd"/>
		<param name="Append" value="true"/>
		<param name="Threshold" value="DEBUG"/>
		<layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss,SSS} %-5p [%c] %m%n"/>
		</layout>
	</appender>

TimeBasedRollingPolicy Example

This Policy is the most flexable way to roll your files over. You can not only customize how often the file rolls over but the policy will allow you to compress your log files using .zip or .gz files.

This Rolling Policy was was originally intended to be in the next version of log4j but the development effort for that stopped. Therefore the log4j team decided to roll it up into an “extra” package. In order to use the log4j-extra package you need to make sure to include the following in your pom.xml file

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>
  1. If you put a “.gz” or “.zip” at the end of your FileNamePattern then the Policy will compress after rolling over.
  2. The FileNamePattern also dictates how often the roll over occurs.
    • %d – daily
    • %d{yyyy-MM} – monthly
    • %d{yyyy-MM-dd} – daily
    • so on and so forth

To use the appender just include line highlighted below in your logger element:

	<logger name="org.vtechfw">
		<level value="debug"></level>
		<appender-ref ref="DRFA"></appender-ref>
	</logger>

In the Java Code

In the above example classes in the org.vtechfw package would print DEBUG messages and above and in the web package INFO messages and above.

Logging In your Source Code

    protected final Log logger = LogFactory.getLog(getClass());

After you define the above attribute just use one of the convenience methods to log.

Fastest Way of Logging

For some Logger logger, writing,

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.

if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

The jury is out on wheather to do this across the board or only with log statements that have string concatination. Its your code. You decide.

Side note:
TOO much logging? Consider implementing a batch logger. It would dump a log to the file only when there is some type of error.

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!

03
Sep
09

How to get Apache Tomcat 6 to log to the console

This site is a collaborative effort! The complete text and sourcecode for this is available on GitHub. Corrections and enhancements are welcome, please make the change and submit a pull request in the comment area below.

s article explains how to get apache tomcat to log to the console (system.out).

The process is quite simple. $CATALINA_HOME represents where you have tomcat version 6 installed.

Step 1

Download Apache log4j and put it into $CATALINA_HOME/lib folder.

Step 2

Download the jar files listed below

  • tomcat-juli-adapters.jar
  • tomcat-juli.jar

You may download them from here (fool proof method):

http://tomcat.apache.org/
Click on version 6 then click on Quick Navigation -> browse -> extras

Not sure if the following link will work years from now: http://www.poolsaboveground.com/apache/tomcat/tomcat-6/v6.0.20/bin/extras/ but it shows you were the files were at the time this blog was being written.

Step 3

Put tomcat-juli.jar into your $CATALINA_HOME/bin folder and the other jar file goes into the $CATALINA_HOME/lib folder.

Step 4

Put the following log4j.properties file into the $CATALINA_HOME/lib folder.

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=INFO, A1

# A1 is set to be a ConsoleAppender.

# A1 uses PatternLayout.
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n

Finally

Restart the tomcat server and you should now start seeing the output to the console.

2009-09-07 10:03:45,208 WARN  org.apache.commons.digester.Digester - [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.jst.j2ee.server:helloWorld' did not find a matching property.
2009-09-07 10:03:45,211 INFO  org.apache.catalina.core.AprLifecycleListener - The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/amd64/server:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/lib/amd64:/usr/lib/jvm/java-6-sun-1.6.0.14/jre/../lib/amd64:/usr/lib64/xulrunner-addons:/usr/java/packages/lib/amd64:/lib:/usr/lib
2009-09-07 10:03:45,231 INFO  org.apache.coyote.http11.Http11Protocol - Initializing Coyote HTTP/1.1 on http-8080
2009-09-07 10:03:45,232 INFO  org.apache.catalina.startup.Catalina - Initialization processed in 316 ms
2009-09-07 10:03:45,254 INFO  org.apache.catalina.core.StandardService - Starting service Catalina
2009-09-07 10:03:45,255 INFO  org.apache.catalina.core.StandardEngine - Starting Servlet Engine: Apache Tomcat/6.0.18
2009-09-07 10:03:45,654 INFO  org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/helloWorld] - Initializing Spring root WebApplicationContext
2009-09-07 10:03:45,768 INFO  org.apache.coyote.http11.Http11Protocol - Starting Coyote HTTP/1.1 on http-8080
2009-09-07 10:03:45,799 INFO  org.apache.jk.common.ChannelSocket - JK: ajp13 listening on /0.0.0.0:8009
2009-09-07 10:03:45,803 INFO  org.apache.jk.server.JkMain - Jk running ID=0 time=0/12  config=null
2009-09-07 10:03:45,804 INFO  org.apache.catalina.startup.Catalina - Server startup in 572 ms

The logging is totally customizable. You can change the where the log output goes to or the Severity of errors that get logged by modifying the log4j.properties file seen above. To learn more about Apache log4j please visit the following URL: http://logging.apache.org

Note that this does not replace the custom logging properties you have within your applications. You can still specify them by putting a log4j.properties inside your applications /WEB-INF/classes folder. This logger is only at the top level. It helps in debugging issues you may encounter with the tomcat server.

This site is a collaborative effort! The complete text and sourcecode for this is available on GitHub. Corrections and enhancements are welcome, please make the change and submit a pull request in the comment area below.



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

Join 75 other followers

April 2017
S M T W T F S
« Mar    
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Blog Stats

  • 807,309 hits