use log4j in junit test assertions

I need to confirm that some error/warnings are logged in the code being tested. The logger is usually private static final that we cannot get from the junit. The following pattern would be able to achieve it.

 

	@Test
	public void testLogMessageIsSent() {
		Logger logger = Logger.getLogger(ClassUnderTest.class);

		ByteArrayOutputStream out = new ByteArrayOutputStream();
		Layout layout = new SimpleLayout();
		Appender appender = new WriterAppender(layout, out);
		logger.addAppender(appender);

		try {
			ClassUnderTest.methodThatLogsAMessage();

			String logMsg = out.toString();

			assertThat("log message should not be null", logMsg, notNullValue());
			assertThat("Should contain warning message", logMsg, containsString(SOME_VALUE));

		} finally {
			logger.removeAppender(appender);
		}
	}

Advertisements

Log4j MDC

I hope that Log4j does not need any introduction and I assume that you already have basic understanding of Log4j. If not, I’ll recommend you to first read this Log4j Tutorial, to get started.

That said, now I’ll start with MDC or Mapped Diagnostic Context. Don’t get scared with this name! MDC is not that tough. It’s a simple yet useful concept. Before I explain what is MDC, lets assume that we are going to develop a simple web application with one servlet MyServlet that serves requests from multiple clients. And, this servlet uses log4j framework for logging. A file appender has been defined for this servlet, so all the log messages will be logged into a text file.

With the above said configuration, all the log messages from MyServletwill go into a single log file. And when this servlet is serving more than one clients at the same time, the log statements will be mixed and there’s  no way to differentiate which log statement is belongs to which client’s processing. This’ll make it difficult to trace and debug if any processing error occured in MyServlet life cycle.

How to differentiate log statements with respective to each clients?

To avoid the log statements mix-in, we could add a user name (or some other data which will be unique to each client) to our log statements. To do this, we have to make sure that we pass this user name data explicitley to each and every log statements, which is a tedious and repetitive work. But, no need to worry! Log4j has an excellent way to overcome this. It’s called asMDC or Mapped Diagnostic Context.

So, What is Log4j MDC (Mapped Diagnostic Context)

To put it simple, the MDC is a map which stores the context data of the particular thread where the context is running. To explain it, come back to our simple application – every client request will be served by different thread of the MyServlet. So, if you use log4j for logging, then each thread can have it’s own MDC which is  global to the entire thread. Any code which is part of that thread can easily access the values that are present in thread’s MDC.

So, how do we make MDC to differentiate logging statements from multiple clients? Simple : Before starting any business process in your code, get the user name (for our Servlet, we can get it from request object) and put that into MDC. Now the user name will be available to the further processing. In your log4j.properties while defining the conversionPattern, add a pattern %X{key} to retrievce the values that are present in the MDC. The key will be userName in our example. It’s like getting a value from a Session object.

 

This is a simple example where we have a one servlet and one filter. The filter intercepts each and every request and put the user name in the MDC.

 

Filter:

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.log4j.MDC;

/**
 * An example authentication filter which is used to intercept all the requests
 * for fetching the user name from it and put the user name to the Log4j Mapped
 * Diagnostic Context (MDC), so that the user name could be used for
 * differentiating log messages.
 *
 * @author veerasundar.com/blog
 *
 */
public class AuthenticationFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        try {
            /*
            * This code puts the value "userName" to the Mapped Diagnostic
            * context. Since MDc is a static class, we can directly access it
            * with out creating a new object from it. Here, instead of hard
            * coding the user name, the value can be retrieved from a HTTP
            * Request object.
            */
            MDC.put("userName", "veera");

            chain.doFilter(request, response);

        } finally {
            MDC.remove("userName");
        }

    }

}

Servlet

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

/**
 * A good-for-nothing servlet which is just writing few messages to the logger
 * object. Since we've configured in the log4j.properties file to include the
 * userName, taken from MDC, every message will be appended with the user name
 * that is set from the AuthenticationFilter
 *
 */
public class Log4jMdcDemo extends HttpServlet {

    private static Logger logger = Logger.getLogger(Log4jMdcDemo.class);

    public Log4jMdcDemo() {
    }

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doService(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doService(request, response);
    }

    protected void doService(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        logger.info("This is  demo for the Log4j MDC concept");
        logger.info("From Veerasundar.com/blog");
        logger.debug("Just some sample messages");
    }
}

Configration

<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" 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>
        <description/>
        <display-name>Log4jMdcDemo</display-name>
        <servlet-name>Log4jMdcDemo</servlet-name>
        <servlet-class>com.veerasundar.code.log4jmdc.Log4jMdcDemo</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Log4jMdcDemo</servlet-name>
        <url-pattern>/Log4jMdcDemo</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>AuthFilter</filter-name>
        <filter-class>com.veerasundar.code.log4jmdc.AuthenticationFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>AuthFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Log4j.properties file which uses the data present in MDC

# sample log4j.properties file, explaining log4j MDC concept
# author: veerasundar.com/blog

log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout

#note the %X{userName} - this is how you fetch data from Mapped Diagnostic Context (MDC)
log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c %x - %m - %X{userName}%n

log4j.rootLogger = DEBUG, consoleAppender

I hope that code explains the concept of MDC. Do let me know if you have any questions/suggestions in the comments.

Output from the log file:

0    [http-8084-2]  INFO Log4jMDCDemo  - This is  demo for the Log4j MDC concept - veera
0    [http-8084-2]  INFO Log4jMDCDemo  - From Veerasundar.com/blog - veera
0    [http-8084-2] DEBUG Log4jMDCDemo  - Just some sample messages - veera

FROM HERE

log4j monitor tool Log4j Web Tracker

It is good tool , git is HERE.

Steps are

    1. add dependency in Maven or build yourself and add to library.
           <dependency>
              <groupId>log4jwebtracker</groupId>
              <artifactId>log4jwebtracker</artifactId>
              <version>1.0.1</version>
          </dependency>
      
    2. add servlet mapping in web.xml
          <servlet>
              <servlet-name>TrackerServlet</servlet-name>
              <servlet-class>log4jwebtracker.servlet.TrackerServlet</servlet-class>
          </servlet>
          <servlet-mapping>
              <servlet-name>TrackerServlet</servlet-name>
              <url-pattern>/tracker/*</url-pattern>
          </servlet-mapping>
      
    3. In the example the tool was mapped as /tracker/*, so if the application is accessible in http://localhost:8080/myapp, the correct URL to access to the tracker is:

      http://localhost:8080/myapp/tracker

 

Just remember to add the appender you need to display in the web UI under the root element in the log4j xml config file.

Root is a special logger.

By specifying a log level in root element, it will contain all the log info in the current app.

Log4J Levels

This log4j post is a tutorial post describing different levels of logging in log4j. This is for log4j beginners only but if you wish to refresh, go ahead and enjoy!

Log4j logger contains three main components namely logger, appender and layout. Logger takes care of the logging mechanism and deals with level of logging. Log4j provides five standard levels of logging. There are two more special levels given by log4j. Above all these, log4j allows you to create custom levels.

 

Five standard log4j levels

DEBUG Level

This log4j level helps developer to debug application. Level of message logged will be focused on providing support to a application developer.

INFO Level

This log4j level gives the progress and chosen state information. This level will be generally useful for end user. This level is one level higher than DEBUG.

WARN Level

This log4j level gives a warning about an unexpected event to the user. The messages coming out of this level may not halt the progress of the system.

ERROR Level

This log4j level gives information about a serious error which needs to be addressed and may result in unstable state. This level is one level higher than WARN.

FATAL Level

This log4j level is straightforward and you don’t get it quite often. Once you get this level and it indicates application death.

Two special log4j levels

ALL Level

This log4j level is used to turn on all levels of logging. Once this is configured and the levels are not considered.

OFF Level

This log4j level is opposite to ALL level. It turns off all the logging.

New Trace Level added in Log4j

TRACE Level

This log4j level gives more detailed information than the DEBUG level and sits top of the hierarchy. This level is introduced from version 1.2.12 in log4j.

Custom log4j levels

Log4j’s levels are mostly sufficient for all common applications. Rarely you may need a new Level apart from the levels provided by log4j. In that case you can extendorg.apache.log4j.Level class and have your own custom level implementation.

Log4j Logger Output

When a logger is created, generally you assign a level. The logger outputs all those messages equal to that level andalso all greater levels than it. So the order for the standard log4j levels are:

Log4J Levels

TRACE Level DEBUG Level INFO Level WARN Level ERROR Level FATAL Level
TRACE Level Y Y Y Y Y Y
DEBUG Level N Y Y Y Y Y
INFO Level N N Y Y Y Y
WARN Level N N N Y Y Y
ERROR Level N N N N Y Y
FATAL Level N N N N N Y
ALL Level Y Y Y Y Y Y
OFF Level N N N N N N

Log4j Level Inheritance Rule

There can be instances when you don’t assign a level to the logger. In such cases log4j handles it seamlessly based on the following level inheritance rule:
The inherited level for a given logger C, is equal to the first non-null level in the logger hierarchy, starting at C and proceeding upwards in the hierarchy towards the root logger.

That means, if you have a package as com.foo.bar and you didn’t allocate a level, then it will inherit the level from com.foo package. Still in that package also if the level is not available, then it will inherit from the log4j root level. The log4j’s root logger is instantiated and available always. Log4j’s root logger by default has DEBUG level.

 

From HEre

log4j的使用

1、 Log4j是什么?
  Log4j可以帮助调试(有时候debug是发挥不了作 用的)和分析,要下载和了解更详细的内容,还是访问其官方网站吧:
 http://jakarta.apache.org/log4j 

2、Log4j的概念
Log4j中有三个主要的组件,它们分别是 Logger、Appender和Layout,Log4j 允许开发人员定义多个Logger,每个Logger拥有自己的名字,Logger之间通过名字来表明隶属关系。有一个Logger称为Root,它永远 存在,且不能通过名字检索或引用,可以通过Logger.getRootLogger()方法获得,其它Logger通过 Logger.getLogger(String name)方法。
Appender则是用来指明将所有的log信息存放到什么地方,Log4j中支持多种appender,如
 console、files、GUI components、NT Event Loggers等,一个Logger可以拥有多个Appender,也就是你既可以将Log信息输出到屏幕,同时存储到一个文件中。
Layout的作用是控制Log信息的输出方式,也就是格式化输出的信息。
Log4j中将要输出的Log信息定义了5种级别,依次为DEBUG、INFO、WARN、ERROR和FATAL,当输出时,只有级别高过配置中规定的 级别的信息才能真正的输出,这样就很方便的来配置不同情况下要输出的内容,而不需要更改代码,这点实在是方便啊。

3、Log4j的配置文件
虽然可以不用配置文件,而在程序中实现配置,但这种方法在如今的系统开发中显然是不可取的,能采用配置文件的地方一定一定要用配置文件。Log4j支持两 种格式的配置文件:XML格式和Java的property格式,本人更喜欢后者,首先看一个简单的例子吧,如下:

  log4j.rootLogger=debug, stdout, R
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller’s file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) – %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log
log4j.appender.R.MaxFileSize= 100KB

# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c – %m%n

  首先,是设置root,格式为 log4j.rootLogger=[level],appenderName, …,其中level就是设置需要输出信息的级别,后面是appender的输出的目的地,appenderName就是指定日志信息输出到哪个地方。您可以同时指定多个输出目的地。 配置日志信息输出目的地Appender,其语法为
  log4j.appender.appenderName = fully.qualified.name.of.appender.class
log4j.appender.appenderName.option1 = value1

log4j.appender.appenderName.option = valueN

Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台)
org.apache.log4j.FileAppender(文件)
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生新文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
配置日志信息的格式(布局),其语法为:
  log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class
log4j.appender.appenderName.layout.option1 = value1
….
log4j.appender.appenderName.layout.option = valueN

Log4j提供的layout有以下几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息

   %p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“\r\n”,Unix平台为“\n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:
 2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)


4、Log4j在程序中的使用 

  要在自己的程序中使用Log4j,首先需要将commons-logging.jar和logging-log4j-1.2.9.jar导入到构建路径中。然后再将log4j.properties放到src根目录下。这样就可以在程序中使用log4j了。在类中使用log4j, 
首先声明一个静态变量 Logger logger=Logger.getLog(“classname”);现在就可以使用了,用法如下:logger.debug(“debug message”)或者logger.info(“info message”),看下面一个小例子: 

  import com.foo.Bar;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class MyApp {
static Logger logger = Logger.getLogger(MyApp.class.getName());
public static void main(String[] args) {
// BasicConfigurator replaced with PropertyConfigurator.
PropertyConfigurator.configure(args[0]);
logger.info(“Entering application.”);
Bar bar = new Bar();
bar.doIt();
logger.info(“Exiting application.”);
}
}