免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1562 | 回复: 0
打印 上一主题 下一主题

在LOG4J中把日志写入远程数据库 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2006-12-20 23:20 |只看该作者 |倒序浏览

LOG4J强大的功能让我爱不释手,为了不用跑到机房看日志,我打算把日志写入远程数据库,这样操作起来就方便了,同时又可以按时间、按关键词搜索,一举两得。
LOG4J
提花了一个JDBCAppender的远程数据库输出方案,使用也很简单,在配置文件里写好驱动名、URL及远程数据库的登陆账号、密码,再加一个布局
&SQL语句全部搞定,真当是方便极了。在我本机上测试没有问题,但欢天喜地移植到服务器上问题就来了,发现每次数据库连接特别慢,严重影响了程序的正常
运行,这还得了,赶紧恢复到原来的状态,思索如何改进。
了解它的原理之后,问题的关键部分就很清楚了,JDBCAppender在向远程数
据库写日志时,用的是短连接,虽然定义了一个BufferSize,但好像不起什么作用,这就相当于每次写日志都要重新建立一次数据连接,而建连接往往最
耗时间的啦,能不能把我原来写的数据库连接池和LOG4J和JDBCAppender结合起来使用呢?
大的方向应该是没有问题,也搜索了一
下别人实现的数据库连接池,但总觉得不是很满意,还是用自己的连接池放心。如何扩展原来的JDBCAppender、把数据库连接池传递进去,一开始没有
搞明白,想了老半天,后来终于整明白了。我只需要继承原来的JDBCAppender,把getConnection()和
closeConnection()两个方法重写即可,其它的都不用变,示例如下:

package com.gftech.log4j;


import java.sql.Connection;

import java.sql.SQLException;

import java.util.ArrayList;


import org.apache.log4j.jdbc.JDBCAppender;


import com.gftech.common.GFConn;

import com.gftech.common.GFDB;



public class JDBCExtAppender extends JDBCAppender ...{

    protected String driver;


    public static GFDB gfdb;


    private ArrayListGFConn> tempList;



    public JDBCExtAppender() ...{

        super();

        tempList = new ArrayListGFConn>();

    }



    /** *//**

     * Override this to return the connection to a pool, or to clean up the

     * resource.

     *

     * The default behavior holds a single connection open until the appender is

     * closed (typically when garbage collected).

     */


    protected void closeConnection(Connection con) ...{


        if (con != null && tempList != null) ...{


            for (int i = 0; i  tempList.size(); i++) ...{

                GFConn gfconn = tempList.get(i);


                if (con==gfconn.getConn()) ...{

                    gfconn.close();

                    tempList.remove(i);

//                    System.err.println("remove conn:"+con);

                    break;

                }

            }

        }

    }



    /** *//**

     * Override this to link with your connection pooling system.

     *

     * By default this creates a single connection which is held open until the

     * object is garbage collected.

     */


    protected Connection getConnection() throws SQLException ...{

        if (gfdb == null)

            gfdb = new GFDB("log4jDB", driver, databaseURL, databaseUser, databasePassword);



        if (gfdb != null) ...{

            GFConn gfconn = gfdb.getConn();


            if (gfconn != null) ...{

                connection = gfconn.getConn();

                tempList.add(gfconn);

            }

        }


        return connection;

    }



    public void setDriver(String driverClass) ...{

        driver = driverClass;


    }


}

然用到了数据库连接池,但我在实际测试中发现,在多线程应用程序中,很快就提示使用的连接无效,但该连接明明在刚开始还可以用,难道连接被主动关闭掉了?
既然用的是数据库连接池,我当然希望所有的连接在连接池内部进行动态管理,外面不需要干涉,这个现象说明JDBCAppender里面一定是显式关闭了连
接.分析出原因后,很容易找到JDBCAppender里面有个close()方法,里面显式关闭了连接:


/** *//**

   * Closes the appender, flushing the buffer first then closing the default

   * connection if it is open.

   */

  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 close() ...{

        flushBuffer();


        this.closed = true;

    }

   

一次测试,发现连接已不会再断开,达到预期效果。同时又发现另外一个问题,就是堆栈信息无法写入到数据库当中,而ConsoleAppender、
FileAppender、SMTPAppender都可以显示异常时的详细堆栈信息,为何JDBCAppender不可以呢?通过查看源代码得知,其它
Appender在最后生成%m对应的信息时都做了一下判断,如果异常被忽略了,则把异常的堆栈信息重新添加进去,而JDBCAppender并没有做这
一步,这是导致它与众不同的原因。搞清楚问题所在之后,我们重写getLogStatement()方法并且JDBCAppender里面也建议重写该方
法,自己对信息做一下处理并为每句添加一个回车换行使信息看起来更清晰,如下所示:


public String getLogStatement(LoggingEvent event)...{

        StringBuffer sbuf=new StringBuffer();

        sbuf.append(layout.format(event));


        if (layout.ignoresThrowable()) ...{

            sbuf.delete(sbuf.length()-2,sbuf.length() );

            String[] s = event.getThrowableStrRep();


            if (s != null) ...{


                for (int j = 0; j  s.length; j++) ...{

                    sbuf.append("\r\n ");

                    sbuf.append(s[j]);

                }

            }

            sbuf.append("')");

        }

        

        return sbuf.toString() ;

    }

在配置文件中做如下设置:

#A5 send log info to remote mysql database

log4j.appender.A5 = com.gftech.log4j.JDBCExtAppender

log4j.appender.A5.Driver = com.mysql.jdbc.Driver

log4j.appender.A5.URL = jdbc:mysql://192.168.10.1:3306/log

log4j.appender.A5.User = root

log4j.appender.A5.Password = plus

log4j.appender.A5.layout = org.apache.log4j.PatternLayout

log4j.appender.A5.sql = INSERT INTO app_log(machine,occur_date,thread_name,cat,level,info) values('DP','%d{yyyy-MM-dd HH:mm:ss}','%t','%c','%p','%m')
指定LOG4J用自定义的扩展JDBC Appender,这样一来大大减少数据库连接建立的次数,提示程序的执行效率
说明:
也可用开源的一些数据库连接池技术,原理都大同小异,同样是修改这两个处理数据库连接的方法可以了
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/16799/showart_218855.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP