Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3354773
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类: Java

2009-11-29 16:46:22

   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.JDBCExtAppender
log4j.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);
        }

    }
   
}

附录2
public 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();  
       
 }  

附录3
public 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
阅读(5722) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~