Wednesday, July 12, 2006

Create your own logging level in log4j


If you need to add your own logging level in log4j, then you can do it as follows. You will have to create your own class which will extend from Level. Here's a sample code for the same:

MyTraceLevel.java:

package org.myapp.log;

import org.apache.log4j.Level;
/**
* My own {@link org.apache.log4j.Level} for logging.
*
* @author Jaikiran Pai
*
*/
public class MyTraceLevel extends Level {

/**
* Value of my trace level. This value is lesser than
* {@link org.apache.log4j.Priority#DEBUG_INT}
* and higher than {@link org.apache.log4j.Level#TRACE_INT}
*/
public static final int MY_TRACE_INT = DEBUG_INT - 10;

/**
* {@link Level} representing my log level
*/
public static final Level MY_TRACE = new MyTraceLevel(MY_TRACE_INT,"MY_TRACE",7);

/**
* Constructor
*
* @param arg0
* @param arg1
* @param arg2
*/
protected MyTraceLevel(int arg0, String arg1, int arg2) {
super(arg0, arg1, arg2);

}

/**
* Checks whether sArg is "MY_TRACE" level. If yes then returns
* {@link MyTraceLevel#MY_TRACE}, else calls
* {@link MyTraceLevel#toLevel(String, Level)} passing it
* {@link Level#DEBUG} as the defaultLevel.
*
* @see Level#toLevel(java.lang.String)
* @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
*
*/
public static Level toLevel(String sArg) {
if (sArg != null && sArg.toUpperCase().equals("MY_TRACE")) {
return MY_TRACE;
}
return (Level) toLevel(sArg, Level.DEBUG);
}

/**
* Checks whether val is {@link MyTraceLevel#MY_TRACE_INT}.
* If yes then returns {@link MyTraceLevel#MY_TRACE}, else calls
* {@link MyTraceLevel#toLevel(int, Level)} passing it {@link Level#DEBUG}
* as the defaultLevel
*
* @see Level#toLevel(int)
* @see Level#toLevel(int, org.apache.log4j.Level)
*
*/
public static Level toLevel(int val) {
if (val == MY_TRACE_INT) {
return MY_TRACE;
}
return (Level) toLevel(val, Level.DEBUG);
}

/**
* Checks whether val is {@link MyTraceLevel#MY_TRACE_INT}.
* If yes then returns {@link MyTraceLevel#MY_TRACE},
* else calls {@link Level#toLevel(int, org.apache.log4j.Level)}
*
* @see Level#toLevel(int, org.apache.log4j.Level)
*/
public static Level toLevel(int val, Level defaultLevel) {
if (val == MY_TRACE_INT) {
return MY_TRACE;
}
return Level.toLevel(val,defaultLevel);
}

/**
* Checks whether sArg is "MY_TRACE" level.
* If yes then returns {@link MyTraceLevel#MY_TRACE}, else calls
* {@link Level#toLevel(java.lang.String, org.apache.log4j.Level)}
*
* @see Level#toLevel(java.lang.String, org.apache.log4j.Level)
*/
public static Level toLevel(String sArg, Level defaultLevel) {
if(sArg != null && sArg.toUpperCase().equals("MY_TRACE")) {
return MY_TRACE;
}
return Level.toLevel(sArg,defaultLevel);
}
}

Now, you will have to configure your log4j.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"debug="false">
<!-- ================================= -->
<!-- Preserve messages in a local file -->
<!-- ================================= -->
<!-- A size based rolling appender -->
<appender name="FILE" class="org.apache.log4j.FileAppender">
<param name="File" value="D:/log/myLogFile.log"/>
<param name="Append" value="false"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c] %m%n"/>
</layout>
</appender>
<!-- ============================== -->
<!-- Append messages to the console -->
<!-- ============================== -->
<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">
<!-- The default pattern: Date Priority [Category] Message\n -->
<param name="ConversionPattern" value="%d{ISO8601} %-5p [%c{1}] %m%n"/>
</layout>
</appender>
<!-- ================ -->
<!-- Limit categories -->
<!-- ================ -->
<category name="org.myapp">
<priority value="MY_TRACE" class="org.myapp.log.MyTraceLevel" />
<appender-ref ref="FILE"/>
</category>
<!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->
<root>
<appender-ref ref="CONSOLE"/>
</root>
</log4j:configuration>


Here's a test program which can be used for testing whether the new log level that you introduced is being identified or not:

TestMyLogLevel.java:

package org.myapp.core;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.myapp.log.MyTraceLevel;

/**
* Tests whether the new log level {@link org.myapp.log.MyTraceLevel#MY_TRACE}
* is working
*
* @author Jaikiran Pai
*
*/
public class TestMyLogLevel {

/**
* Writes a log message with {@link org.myapp.log.MyTraceLevel#MY_TRACE}
* and another message with {@link Level#DEBUG}
*
* @param args
*/
public static void main(String[] args) {
Logger logger = Logger.getLogger(TestMyLogLevel.class);
logger.log(MyTraceLevel.MY_TRACE,"I am MY_TRACE log");
logger.log(Level.DEBUG ,"I am a debug message");
}
}


Finally, here's the log file that got generated:

2006-07-12 13:45:40,633 MY_TRACE [org.myapp.core.TestMyLogLevel] I am MY_TRACE log
2006-07-12 13:45:40,633 DEBUG [org.myapp.core.TestMyLogLevel] I am a debug message

Points to note:

- The int value that you specify for your log level is important. Here i have defined "MY_TRACE" log level is to be higher than the DEBUG level but lower than the TRACE level provided by log4j. So whenever you have set a priority level to DEBUG on the category(in your log4j.xml file), the MY_TRACE level logs will *NOT* make it to the log file.

8 comments:

Srijith Unni said...

Hi Jai, I tried this.. however i was able to notice that only when the MY_TRACE_INT value is set to Level.DEBUG_INT + 1 it worked.. not with Level.DDEBUG_INT - 10. Could you please check..?

Simon pascal said...

Hi i am french spoken i try to write english thanck i am interess to your code on log4j i write it it's not do any thing can you give me your all source code?

Jaikiran said...

Simon,

The code that i posted is all that i had in the application. There was nothing more than these files. What issue are you running into?

Faris said...

As Srijith told after a long try i noticed that When DEBUG_INT + 1 given its working not DEBUG_INT -10.
Now I need to configure this custom level for sending email. How can i do this. Please help me..

Anonymous said...

Hello, how to configured a properties file please ?

Panay said...

Hi Jai, Can you please let me know where and how to refer log properties from the custom logger class. I have a constraint that all property files should be maintained under a particular directory structure.

Jaikiran said...

Panay, do you mean the log configuration properites file? It should be in the classpath of the application. For example, if you are deploying a .war file then placing that properties file in .war/WEB-INF/classes folder makes it available in the classpath.

Xpmich said...

Hi,
I tried this.
With console appender, no problem.
How can you do with others appenders, as socketAppender or fileAppender, please ?

Note : all of my appender are create by code, i don't use xml or properties.