官方API地址:http://logging.apache.org/log4j/1.2/apidocs/index.html?org/apache/log4j/PatternLayout.html
控制台的实现就不说了,这里提供两种实例的配置,一种是输出为文件的(每天输出一个文件),一种为输出到数据库的配置。
1、输出到文件:
log4j.rootCategory=WARN, CONSOLE, FILE log4j.logger.com.surfilter.bt=FATAL,TOFILE log4j.appender.TOFILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.TOFILE.Threshold=FATAL log4j.appender.TOFILE.File=E:/javascpace/bt/logs/union.html log4j.appender.TOFILE.Append=true log4j.appender.TOFILE.ImmediateFlush=true log4j.appender.TOFILE.DatePattern='.'yyyy-MM-dd'.html' log4j.appender.TOFILE.layout=com.surfilter.bt.util.FormatHTMLLayout
这里的com.surfilter.bt.util.FormatHTMLLayout是重写了log4j提供的HTMLLayout类,具体代码如下:
import java.text.SimpleDateFormat; import java.util.Map; import org.apache.log4j.HTMLLayout; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.helpers.Transform; import org.apache.log4j.spi.LocationInfo; import org.apache.log4j.spi.LoggingEvent; import com.opensymphony.xwork2.ActionContext; import com.surfilter.core.Constants; import com.surfilter.security.domain.User; public class FormatHTMLLayout extends HTMLLayout { public FormatHTMLLayout() { } protected final int BUF_SIZE = 256; protected final int MAX_CAPACITY = 1024; static String TRACE_PREFIX = "<br> "; // output buffer appended to when format() is invoked private StringBuffer sbuf = new StringBuffer(BUF_SIZE); String title="系统操作日志"; /** * A string constant used in naming the option for setting the the HTML * document title. Current value of this string constant is <b>Title</b>. */ public static final String TITLE_OPTION = "Title"; // Print no location info by default boolean locationInfo = true; 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(String.valueOf(i)); sbuf.append("</td>" + Layout.LINE_SEP); */ sbuf.append("<td>"); 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=\"级别\">"); if (event.getLevel().equals(Level.FATAL)) { 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().substring(event.getLoggerName().lastIndexOf("."))); sbuf.append("<td title=\"类名\">"); sbuf.append(escapedLogger); sbuf.append("</td>" + Layout.LINE_SEP); */ if (locationInfo) { LocationInfo locInfo = event.getLocationInformation(); sbuf.append("<td title=\"行号\">"); sbuf.append(Transform.escapeTags(locInfo.getFileName())); sbuf.append(':'); sbuf.append(locInfo.getLineNumber()); sbuf.append("</td>" + Layout.LINE_SEP); } Map session = ActionContext.getContext().getSession(); if(session!=null){ User user = (User) session.get(Constants.USER_IN_SESSION); sbuf.append("<td>"+user.getName()+"</td>"); }else{ sbuf.append("<td> </td>"); } sbuf.append("<td title=\"日志信息\">"); 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=\"4\">"); appendThrowableAsHTML(s, sbuf); sbuf.append("</td></tr>" + Layout.LINE_SEP); } return sbuf.toString(); } private 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); } } } /** * Returns appropriate HTML headers. */ 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("<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">"); 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: 12px;}" + 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 SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new java.util.Date()) + "<br>" + Layout.LINE_SEP); // sbuf.append("<p>" + 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>序列</th>" + Layout.LINE_SEP); sbuf.append("<th>执行时间</th>" + Layout.LINE_SEP); sbuf.append("<th>级别</th>" + Layout.LINE_SEP); // sbuf.append("<th>所在类</th>" + Layout.LINE_SEP); if (locationInfo) { sbuf.append("<th>所在行</th>" + Layout.LINE_SEP); } sbuf.append("<th>操作人</th>"); sbuf.append("<th>信息</th>" + Layout.LINE_SEP); sbuf.append("</tr>" + Layout.LINE_SEP); sbuf.append("<br></br>" + Layout.LINE_SEP); return sbuf.toString(); } }
注,上面输出里包含了从当前session里取到的用户信息,只需要重写getHeader,format,appendThrowableAsHTML三个方法就可以了。LoggingEvent 提供的方法可以获取各种日志信息。
2、输入到数据库,现在配置是将日志信息同时输出到文件和数据库
log4j.rootCategory=WARN, CONSOLE, FILE log4j.logger.com.surfilter.bt=FATAL,TOFILE,JDBC log4j.appender.TOFILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.TOFILE.Threshold=FATAL log4j.appender.TOFILE.File=E:/javascpace/bt/logs/union.html log4j.appender.TOFILE.Append=true log4j.appender.TOFILE.ImmediateFlush=true log4j.appender.TOFILE.DatePattern='.'yyyy-MM-dd'.html' log4j.appender.TOFILE.layout=com.surfilter.bt.util.FormatHTMLLayout
log4j.appender.JDBC=org.apache.log4j.jdbc.JDBCAppender #log4j.appender.JDBC=com.surfilter.bt.util.Log4jToDBAppender log4j.appender.JDBC.Threshold=FATAL log4j.appender.JDBC.URL=jdbc:oracle:thin:@127.0.0.1:1521:orcl log4j.appender.JDBC.driver=oracle.jdbc.driver.OracleDriver log4j.appender.JDBC.user=db_user log4j.appender.JDBC.password=db_password log4j.appender.JDBC.sql=insert into sys_log(id,loginid,PRIORITY,LOGDATE,CLASS,METHOD,MSG) values (seq_sys_log.nextval,'0','%p','%d{yyyy-MM-dd HH:mm:ss}','%c{1}','%-10.50l','%m') log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
以上配置将会同时输出到文件和数据库,注意这里有一行注释掉的实现com.surfilter.bt.util.Log4jToDBAppender,如果你要使用连接池可以继承JDBCAppender重写实现,下面是JDBCAppender的实现源码,可以从log4j官网上down到:
package org.apache.log4j.jdbc; import org.apache.log4j.spi.*; import org.apache.log4j.PatternLayout; import java.util.ArrayList; import java.util.Iterator; import java.sql.DriverManager; import java.sql.Connection; import java.sql.Statement; import java.sql.SQLException; public class JDBCAppender extends org.apache.log4j.AppenderSkeleton implements org.apache.log4j.Appender { protected String databaseURL = "jdbc:odbc:myDB"; protected String databaseUser = "me"; protected String databasePassword = "mypassword"; protected Connection connection = null; protected String sqlStatement = ""; protected int bufferSize = 1; protected ArrayList buffer; protected ArrayList removes; public JDBCAppender() { super(); buffer = new ArrayList(bufferSize); removes = new ArrayList(bufferSize); } public void append(LoggingEvent event) { buffer.add(event); if (buffer.size() >= bufferSize) flushBuffer(); } protected String getLogStatement(LoggingEvent event) { return getLayout().format(event); } protected void execute(String sql) throws SQLException { Connection con = null; Statement stmt = null; try { con = getConnection(); stmt = con.createStatement(); stmt.executeUpdate(sql); } catch (SQLException e) { if (stmt != null) stmt.close(); throw e; } stmt.close(); closeConnection(con); //System.out.println("Execute: " + sql); } protected void closeConnection(Connection con) { } protected Connection getConnection() throws SQLException { if (!DriverManager.getDrivers().hasMoreElements()) setDriver("sun.jdbc.odbc.JdbcOdbcDriver"); if (connection == null) { connection = DriverManager.getConnection(databaseURL, databaseUser, databasePassword); } return connection; } public void close() { flushBuffer(); try { if (connection != null && !connection.isClosed()) connection.close(); } catch (SQLException e) { errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE); } this.closed = true; } public void flushBuffer() { //Do the actual logging removes.ensureCapacity(buffer.size()); for (Iterator i = buffer.iterator(); i.hasNext();) { try { LoggingEvent logEvent = (LoggingEvent)i.next(); String sql = getLogStatement(logEvent); execute(sql); removes.add(logEvent); } catch (SQLException e) { errorHandler.error("Failed to excute sql", e, ErrorCode.FLUSH_FAILURE); } } // remove from the buffer any events that were reported buffer.removeAll(removes); // clear the buffer of reported events removes.clear(); } public void finalize() { close(); } public boolean requiresLayout() { return true; } public void setSql(String s) { sqlStatement = s; if (getLayout() == null) { this.setLayout(new PatternLayout(s)); } else { ((PatternLayout)getLayout()).setConversionPattern(s); } } public String getSql() { return sqlStatement; } public void setUser(String user) { databaseUser = user; } public void setURL(String url) { databaseURL = url; } public void setPassword(String password) { databasePassword = password; } public void setBufferSize(int newBufferSize) { bufferSize = newBufferSize; buffer.ensureCapacity(bufferSize); removes.ensureCapacity(bufferSize); } public String getUser() { return databaseUser; } public String getURL() { return databaseURL; } public String getPassword() { return databasePassword; } public int getBufferSize() { return bufferSize; } public void setDriver(String driverClass) { try { Class.forName(driverClass); } catch (Exception e) { errorHandler.error("Failed to load driver", e, ErrorCode.GENERIC_FAILURE); } } }
然后说一下PatternLayout里的ConversionPattern主要的格式化输开形式,Log4J采用类似C语言中的printf函数的打印,格式化日志信息,打印参数如下:
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名,例%c{1}: 类名"a.b.c" 时输出 "c"
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比 如:%d{yyyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。