log4j写数据库,通常只能写入log4j提供的信息,如果用来记录用户ID号,操作等的记录,则无法实现.
这里,我在log4j里加了一个字段userID (当然你可以再加几个)用来记录用户ID,操作
1.log4j配置文件#向控制台和数据库输出
log4j.rootLogger=DEBUG,stdout,JDBC
log4j.addivity.org.apache=true
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[Datetime]%d{yyyy-MM-dd hh:mm:ss}%n[Priority]%p%n[Location]%l%n[Message]%m%n
log4j.appender.JDBC.Threshold=INFO
log4j.appender.JDBC=
xaut.common.log.JDBCExtAppenderlog4j.appender.JDBC.driver=oracle.jdbc.driver.OracleDriver
log4j.appender.JDBC.URL=jdbc:oracle:thin:@192.168.88.26:1521:hdptdb
log4j.appender.JDBC.user=hdptdep
log4j.appender.JDBC.password=123456
log4j.appender.JDBC.layout=org.apache.log4j.PatternLayout
log4j.appender.JDBC.sql=INSERT INTO SYS_LOG(LOGTIME,LOGLEVEL,LOCATION,MESSAGE,USERID)VALUES('%d{yyyy-MM-dd HH:mm:ss}','%p','%l','%m', 大家注意最后一句
log4j.appender.JDBC.sql ,这个sql语句以逗号结束,并没有写完,缺少最后一个字段userID的值(这个值由程序填写)。
2.数据库表结构 我是在oracle下测试的,表结构如下
create table SYS_LOG
(
LOGTIME VARCHAR2(32),
USERID VARCHAR2(20),
LOGLEVEL VARCHAR2(10),
LOCATION VARCHAR2(80),
MESSAGE VARCHAR2(100)
)
3.数据库连接池和自定义信息 org.apache.log4j.jdbc.JDBCAppender是log4j提供的默认向数据库插入数据的类。这个类采用的是直接通过JDBC连接数据库,效率低,而且不能插入自定义信息。
如果你的服务器上配置了数据库连接池,而且想插入自定义信息,比如用户ID,必须重载此类。
代码见附录1
说明:
(1)网上大量的人说至少重载三个方法getconnect,closeconnection,excute,我看了一下源代码,悲从心生。最早应该有一个程序员,对面向对象技术是懂非懂,看了一些资料就草率的下了这个结论,而且还想当然的结出了例子代码。
比如:
如何给 Log4j 配上数据库连接池 从面向对象技术上看,重载意味着先当实例化出一个子类对象时,如果子类重载了父类的方法,则执行子类的方法,如果没有重载,则执行父类的方法。
也就是说你觉得哪个方法达不到你的要求,你就要定制自己的实现代码。很多人把JDBCAppender中的成员变量和方法又重新在自己的类里面又写了一遍,呵呵。
(2)由于我们使用了数据库连接池,所以必须重载getConnection和closeConnection
(3)由于我们要加入自定义信息,必须重载getLogStatement
(4)DatabaseConfigure类请在博客内找,这里就不提供超链接了
4.添加自定义信息 (1)先添加一个ParameterizedMessage接口,见附录2
(2)再用JDBCLogMessage类实现
ParameterizedMessage接口,见附录3
(3)JDBCExtAppender类重载getLogStatement()方法,在SQL字符串的最后添加用户ID等信息,将SQL语句补充完整
(4)有的人很巧妙,通过PreparedStatement完成了SQL数据的插入,但是彻底影响了log4j的整体结构,这里不推荐大家使用。参见《》
这个重载的最根本问题,是重新定义了getLogStatement方法的功能(从提供合法的SQL语句变成了直接进行SQL操作),由于log4j是通常的类库,又不是你自己写的,会引起致命的错误的。
5.测试代码public class Logtest {
static Logger logger = Logger.getLogger(Logtest.class.getName());
public static void main(String[] args) {
ParameterizedMessage msg = new JDBCLogMessage("question error","1001");
logger.error(msg);
}
}
说明:
1001是指用户ID
惟一美中不足是message字段里会显示
[question error, 1001]
多一个中括号和1001信息,呵呵
附录1:public class JDBCExtAppender extends org.apache.log4j.jdbc.JDBCAppender {
public JDBCExtAppender() {
super();
}
/**
* 重载JDBCAppender的getConnection()方法
*/
protected Connection getConnection() throws SQLException {
if (DatabaseConfigure.getInstance().getDatapool() != null) {
try{
Context initCtx = new InitialContext();
DataSource ds = (DataSource) initCtx.lookup(DatabaseConfigure.getInstance().getDatapool());
if (ds != null)
this.connection = ds.getConnection();
}catch(NamingException namingex){
namingex.printStackTrace();
throw new SQLException("-datapool init error ");
}
} else {
//如果没有数据库连接池,则直接连接
try {
Class.forName(DatabaseConfigure.getInstance().getJdbcDriver());
} catch (ClassNotFoundException e) {
System.out.println(" class not found: " + e.getMessage());
e.printStackTrace();
throw new SQLException("-Database driver notFind ");
}
try {
this.connection = DriverManager.getConnection(DatabaseConfigure.getInstance()
.getDatabaseURL(), DatabaseConfigure.getInstance()
.getDatabaseName(), DatabaseConfigure.getInstance()
.getDatebasePassword());
} catch (SQLException sqlex) {
System.err.println("DatabaseBean connection error"
+ sqlex.getMessage());
sqlex.printStackTrace();
throw new SQLException("-Database connection error ");
}
}
return this.connection;
}
/**
* 重载getLogStatement()方法,
* 在SQL字符串最后添加用户ID等信息
*/
protected String getLogStatement(LoggingEvent event) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(layout.format(event));
if (event.getMessage() instanceof ParameterizedMessage) {
//检测SQL的最后一个字符是不是逗号,如果不是,则在这里补上
if(stringBuffer.charAt(stringBuffer.length()-1)!=',')
stringBuffer.append(",");
Object[] params = ((ParameterizedMessage) event.getMessage()).getParameters();
for (int i = 1; i < params.length; i++) {
stringBuffer.append(params[i]);
stringBuffer.append(",");
}
stringBuffer.deleteCharAt(stringBuffer.length()-1);
stringBuffer.append(")");
}
return stringBuffer.toString();
}
/**
* 重载JDBCAppender的方法,取消与数据库的连接
*/
protected void closeConnection(Connection con) {
try {
if (connection != null && !connection.isClosed())
connection.close();
} catch (SQLException e) {
errorHandler.error("Error closing connection", e,
ErrorCode.GENERIC_FAILURE);
}
}
}
附录2public interface ParameterizedMessage extends Serializable {
/**
* 获取参数列表
* @return 返回参数列表
*/
public Object[] getParameters();
/**
* 获取指定索引位置的参数
* @param index 索引位置
* @return 返回参数列表中指定索引位置的参数值
* @throws IndexOutOfBoundsException 当index >= 参数列表个数时,抛出此异常
* @see getParameterCount()
*/
public Object getParameter(int index) throws IndexOutOfBoundsException;
/**
* 获取参数个数
* @return 返回参数个数
*/
public int getParameterCount();
}
附录3public class JDBCLogMessage implements ParameterizedMessage {
private static final long serialVersionUID = 1709063421963292637L;
private Object[] params;
public JDBCLogMessage(Object... params) {
this.params = params;
}
public Object[] getParameters() {
return this.params;
}
public Object getParameter(int index) throws IndexOutOfBoundsException {
return this.params[index];
}
public int getParameterCount() {
return this.params.length;
}
@Override
public String toString() {
return Arrays.toString(this.params);
}
}
参考文献1.Log4j记录日志到数据库.
2.如何给 Log4j 配上数据库连接池. http://hi.baidu.com/hwaspf/blog/item/e1583dc73562861c9c163d29.html