自定义log4j的HTML日志输出格式

Sat Feb 14 01:15:50 CST 2015 2845 Java

文章摘要在web应用中,日志尤为重要。在java领域中,log4j就是一款优秀的第三方日志工具。我喜欢使用log4j的html格式来保存日志文件,因为它看上去更舒服。但有些时候,需要灵活的修改html格式输出的内容,由于log4j似乎没有提供这样的方法,令不少开发者面临懊恼。而本文介绍的内容,是另辟蹊径的修改HTMLLayout的方法。

log4j一般给使用者提供四种日志输出格式:

org.apache.log4j.HTMLLayout(以HTML表格形式布局),

org.apache.log4j.PatternLayout(可以灵活地指定布局模式),

org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),

org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)

当log4j的Appender(日志输出目的地)被配置为文件,且选择使用org.apache.log4j.HTMLLayout格式时,日志信息将呈现为

效果截图

可以看到,org.apache.log4j.HTMLLayout格式包含Time(自日志系统启动到输出该条日志信息经过的时间)、输出该条信息的Thread、日志级别Level、类名以及输出的Message.

有些时候为了方便,我们希望把该html文件(日志记录输出的文件)的Time信息修改为“yyyy-MM-dd hh:mm:ss”格式。但令人懊恼的是,log4j似乎没有给使用者提供修改org.apache.log4j.HTMLLayout输出的Time格式的途径。但山穷水复疑无路,柳暗花明又一村,面向对象的世界里更是如此。我们可以通过修改org.apache.log4j.HTMLLayout这个类,来修改Time的格式。(当然,我们也可以通过继承log4j的Layout来自己编写一个符合特定需要的格式,但前提是你对log4j有足够的了解并且你愿意付出时间)


下面我们来试验一下修改org.apache.log4j.HTMLLayout

一、在Eclipse新建一个Java project;导入log4j的jar包log4j-1.x.xx.jar;创建Main.java;

import org.apache.log4j.Logger;
 
public class Main {
     
    public static void main(String[] args) {
        //通过Logger工厂得到一个一个Logger
        Logger log = Logger.getLogger(Main.class);
         
        //输出日志info信息
        log.info("Hello log4j.");
        log.info("This is my custom HTMLLayout.");
    }
 
}

二、在src目录下新建一个log4j.properties,在D盘下新建一个log文件夹。(stdout输出源可以不要)

log4j.rootLogger=info,logFile,stdout
log4j.appender.logFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.logFile.File=D\://log/log.html
log4j.appender.logFile.layout=MyHTMLLayout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.TTCCLayout

三、找到log4j的源码目录(/apache-log4j/src/main/java/org/apache/log4j)下的HTMLLayout.java,把它复制到自己的工程中。

四、在复制到项目中的这个java文件中找到public String format(LogginEvent event)方法的sbuf.append(event.timeStamp - LoggingEvent.getStartTime()),注释掉。在原位置加上sbuf.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));(下面代码76、77行处)。为了便于区分,最好重命名一下你工程里的这个HTMLLayout.java,我将其重命名为了MyHTMLLayout.java.

import java.text.SimpleDateFormat;
import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.helpers.Transform;
 
public class MyHTMLLayout extends Layout {
 
  protected final int BUF_SIZE = 256;
  protected final int MAX_CAPACITY = 1024;
 
  static String TRACE_PREFIX = "<br>&nbsp;&nbsp;&nbsp;&nbsp;";
 
  // output buffer appended to when format() is invoked
  private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
 
   
  public static final String LOCATION_INFO_OPTION = "LocationInfo";
 
   
  public static final String TITLE_OPTION = "Title";
 
  // Print no location info by default
  boolean locationInfo = false;
 
  String title = "Log4J Log Messages";
 
   
  public
  void setLocationInfo(boolean flag) {
    locationInfo = flag;
  }
 
  
  public
  boolean getLocationInfo() {
    return locationInfo;
  }
 
  
  public
  void setTitle(String title) {
    this.title = title;
  }
 
  
  public
  String getTitle() {
    return title;
  }
 
 
  public
  String getContentType() {
    return "text/html";
  }
 
   
  public
  void activateOptions() {
  }
 
  public
  String format(LoggingEvent event) {
 
    if(sbuf.capacity() > MAX_CAPACITY) {
      sbuf = new StringBuffer(BUF_SIZE);
    } else {
      sbuf.setLength(0);
    }
 
    sbuf.append(Layout.LINE_SEP + "<tr>" + Layout.LINE_SEP);
 
    sbuf.append("<td>");
    //被修改处
    //sbuf.append(event.timeStamp - LoggingEvent.getStartTime());
    sbuf.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));//新增的
    sbuf.append("</td>" + Layout.LINE_SEP);
 
    String escapedThread = Transform.escapeTags(event.getThreadName());
    sbuf.append("<td title=\"" + escapedThread + " thread\">");
    sbuf.append(escapedThread);
    sbuf.append("</td>" + Layout.LINE_SEP);
 
    sbuf.append("<td title=\"Level\">");
    if (event.getLevel().equals(Level.DEBUG)) {
      sbuf.append("<font color=\"#339933\">");
      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
      sbuf.append("</font>");
    }
    else if(event.getLevel().isGreaterOrEqual(Level.WARN)) {
      sbuf.append("<font color=\"#993300\"><strong>");
      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
      sbuf.append("</strong></font>");
    } else {
      sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
    }
    sbuf.append("</td>" + Layout.LINE_SEP);
 
    String escapedLogger = Transform.escapeTags(event.getLoggerName());
    sbuf.append("<td title=\"" + escapedLogger + " category\">");
    sbuf.append(escapedLogger);
    sbuf.append("</td>" + Layout.LINE_SEP);
 
    if(locationInfo) {
      LocationInfo locInfo = event.getLocationInformation();
      sbuf.append("<td>");
      sbuf.append(Transform.escapeTags(locInfo.getFileName()));
      sbuf.append(':');
      sbuf.append(locInfo.getLineNumber());
      sbuf.append("</td>" + Layout.LINE_SEP);
    }
 
    sbuf.append("<td title=\"Message\">");
    sbuf.append(Transform.escapeTags(event.getRenderedMessage()));
    sbuf.append("</td>" + Layout.LINE_SEP);
    sbuf.append("</tr>" + Layout.LINE_SEP);
 
    if (event.getNDC() != null) {
      sbuf.append("<tr><td bgcolor=\"#EEEEEE\" style=\"font-size : xx-small;\" colspan=\"6\" title=\"Nested Diagnostic Context\">");
      sbuf.append("NDC: " + Transform.escapeTags(event.getNDC()));
      sbuf.append("</td></tr>" + Layout.LINE_SEP);
    }
 
    String[] s = event.getThrowableStrRep();
    if(s != null) {
      sbuf.append("<tr><td bgcolor=\"#993300\" style=\"color:White; font-size : xx-small;\" colspan=\"6\">");
      appendThrowableAsHTML(s, sbuf);
      sbuf.append("</td></tr>" + Layout.LINE_SEP);
    }
 
    return sbuf.toString();
  }
 
  void appendThrowableAsHTML(String[] s, StringBuffer sbuf) {
    if(s != null) {
      int len = s.length;
      if(len == 0)
    return;
      sbuf.append(Transform.escapeTags(s[0]));
      sbuf.append(Layout.LINE_SEP);
      for(int i = 1; i < len; i++) {
    sbuf.append(TRACE_PREFIX);
    sbuf.append(Transform.escapeTags(s[i]));
    sbuf.append(Layout.LINE_SEP);
      }
    }
  }
 
   
  public
  String getHeader() {
    StringBuffer sbuf = new StringBuffer();
    sbuf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"  + Layout.LINE_SEP);
    sbuf.append("<html>" + Layout.LINE_SEP);
    sbuf.append("<head>" + Layout.LINE_SEP);
    sbuf.append("<title>" + title + "</title>" + Layout.LINE_SEP);
    sbuf.append("<style type=\"text/css\">"  + Layout.LINE_SEP);
    sbuf.append("<!--"  + Layout.LINE_SEP);
    sbuf.append("body, table {font-family: arial,sans-serif; font-size: x-small;}" + Layout.LINE_SEP);
    sbuf.append("th {background: #336699; color: #FFFFFF; text-align: left;}" + Layout.LINE_SEP);
    sbuf.append("-->" + Layout.LINE_SEP);
    sbuf.append("</style>" + Layout.LINE_SEP);
    sbuf.append("</head>" + Layout.LINE_SEP);
    sbuf.append("<body bgcolor=\"#FFFFFF\" topmargin=\"6\" leftmargin=\"6\">" + Layout.LINE_SEP);
    sbuf.append("<hr size=\"1\" noshade>" + Layout.LINE_SEP);
    sbuf.append("Log session start time " + new java.util.Date() + "<br>" + Layout.LINE_SEP);
    sbuf.append("<br>" + Layout.LINE_SEP);
    sbuf.append("<table cellspacing=\"0\" cellpadding=\"4\" border=\"1\" bordercolor=\"#224466\" width=\"100%\">" + Layout.LINE_SEP);
    sbuf.append("<tr>" + Layout.LINE_SEP);
    sbuf.append("<th>Time</th>" + Layout.LINE_SEP);
    sbuf.append("<th>Thread</th>" + Layout.LINE_SEP);
    sbuf.append("<th>Level</th>" + Layout.LINE_SEP);
    sbuf.append("<th>Category</th>" + Layout.LINE_SEP);
    if(locationInfo) {
      sbuf.append("<th>File:Line</th>" + Layout.LINE_SEP);
    }
    sbuf.append("<th>Message</th>" + Layout.LINE_SEP);
    sbuf.append("</tr>" + Layout.LINE_SEP);
    return sbuf.toString();
  }
 
   
  public
  String getFooter() {
    StringBuffer sbuf = new StringBuffer();
    sbuf.append("</table>" + Layout.LINE_SEP);
    sbuf.append("<br>" + Layout.LINE_SEP);
    sbuf.append("</body></html>");
    return sbuf.toString();
  }
 
  
  public
  boolean ignoresThrowable() {
    return false;
  }
}

五、下一步,我想大家应该都能想到了:修改工程src目录里的配置文件log4j.properties,将输出到文件的Appender的格式修改为我们自定义的MyHTMLLlayout.


陈祖煌的博客

六、运行一下Main.java,你会看到D盘的log文件夹里多了一个log.html文件,双击打开,有惊喜。


可以看到,第一列的Time已经变成了我们自定义的yyyy-MM-dd hh:mm:ss.至此,本次试验完成了。



依葫芦画瓢,你还可以修改HTMLLayout的其他部分,也可以修改其他layout.


该文章本人首发于http://my.oschina.net/chenzuhuang/blog/348732

打赏
打赏

分享到: