log4j+数据库连接池添加自定义信息+自定义SQL

from :http://blog.chinaunix.net/space.php?uid=122937&do=blog&id=142939

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的整体结构,这里不推荐大家使用。参见《 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记录日志到数据库. http://www.hxhack.com/bbs/read.php?tid-324467.html
2.如何给 Log4j 配上数据库连接池. http://hi.baidu.com/hwaspf/blog/item/e1583dc73562861c9c163d29.html
需求说明 (1)使用UserDaoImp1类的方法查找用户,并用User类的getUserInfo()方法输出用户信息 (2)使用一个不存在的用户名查找用户,使用try-catch对抛出的异常进行处理 实现思路及关键代码 (1)在测试类中调用UserDaoImp类的addUser(User user)方法,添加用户,然后用findUser(String uName)方法查找并输出用户信息 (2)在测试类中调用UserDaoImp1类的findUser(String uName)方法,使用不存在的用户名查找用户,并试图输出用户信息 (3)对抛出的异常使用try-catch进行异常处理。 实践二:使用try-catch-finally进行异常处理 需求说明 (1)对实践1的异常使用try-catch-finally进行异常处理 (2)在finally块输出是否抛出了异常 实现思路及关键代码 (1)在任务一中的代码上增加finally块 (2)为了判断在finally块输出是否抛出异常,可以设置一个变量,在catch块里修改这个变量 实践三:使用throw和throws 需求说明 修改UserDaoImpl类的updateUser(User user)方法,要求如果用户id被修改,则: (1)不执行更新 (2)抛出一个Exception异常 (3)异常消息是“用户id不能修改” 实现思路及关键代码 (1)修改UserDao类的updateUser(User user)方法,声明抛出异常 (2)修改UserDaoImpl类的updateUser(User user)方法,加入判断语句,并抛出异常 (3)在测试类中调用,并进行异常处理 实践四:使用log4j 需求说明 (1)使用log4j输出日志信息 (2)查看输出日志信息
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值