`

在LOG4J中把日志写入远程数据库

阅读更多
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 ArrayList<GFConn> tempList;

    public JDBCExtAppender() ...{
        super();
        tempList = new ArrayList<GFConn>();
    }

    /** *//**
     * 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,这样一来大大减少数据库连接建立的次数,提示程序的执行效率

说明:

也可用开源的一些数据库连接池技术,原理都大同小异,同样是修改这两个处理数据库连接的方法可以了
分享到:
评论
2 楼 zheng12tian 2012-07-03  
代码有全的不?
只贴一部分,,,,
1 楼 ajie1986 2010-04-27  
lz这种发现问题,解决问题,不断改进的做法和想法很赞

相关推荐

    log4j日志jar包

    本文将详细探讨Log4j日志jar包的功能、使用方法以及其在实际项目中的重要性。 1. **Log4j的基本概念** Log4j是一个灵活且功能强大的日志库,它提供了丰富的配置选项,可以控制日志信息的输出级别(如DEBUG、INFO...

    log4j jar包

    Log4j不仅支持标准输出,如控制台,还可以写入文件、数据库,甚至通过网络发送,极大地增强了日志的管理和分析能力。 二、核心组件 1. **Logger**:日志记录器是Log4j的核心组件,负责接收日志消息并决定是否记录...

    log4j中的dtd文件

    本文将详细讨论标题中提及的“log4j中的dtd文件”及其在日志管理中的作用。 首先,我们需要理解什么是DTD(Document Type Definition)。DTD是一种定义XML文档结构的语言,它规定了XML文档中元素和属性的规则。在...

    log4j_jar包跟教程说明

    - Log4j允许开发者创建自定义的Appender和Layout来满足特定需求,如发送邮件、写入数据库等。 - Appender决定了日志信息的输出位置,而Layout则决定日志的显示格式。 6. **Log4j的最新版本:Log4j 2** - 随着...

    log4j-slf4j-impl.zip

    4. **插件体系**:Log4j拥有强大的插件系统,如Appender和Layout,可以根据需要扩展功能,如发送日志到远程服务器、写入数据库等。 5. **MDC与NDC**:Log4j-SLF4J-Impl支持Mapped Diagnostic Context (MDC) 和...

    Log4j jar包.rar

    这个"Log4j jar包.rar"压缩文件很可能包含了Log4j库的JAR文件,供Java开发者在他们的项目中引用。 **Log4j的核心组件:** 1. **配置文件**:Log4j的配置通常通过XML、JSON或.properties文件进行,定义了日志信息的...

    log4j2用户指南

    Log4j2 支持日志分离,即在一个应用程序中使用多个独立的日志记录配置: - **Separate ClassLoaders**:使用不同的类加载器隔离日志配置。 - **Separate Contexts**:每个配置使用独立的 LoggerContext。 #### 15....

    log4j-1.2.17.jar

    4. **Appender与Layout**:在Log4j中,Appender负责将日志信息输出到特定目的地,而Layout则决定日志信息的格式。常见的Appender有ConsoleAppender(控制台)、FileAppender(文件)和SMTPAppender(电子邮件),...

    Android log4j

    6. **日志输出**:在Android环境中,`log4j`可能无法像在服务器端那样直接写入文件,但可以通过自定义Appender将日志发送到`Logcat`,或者通过网络发送到远程服务器。 7. **注意兼容性**:虽然`log4j`是为Java设计...

    log4j自定义

    本文将详细探讨如何在Log4j中进行自定义配置,以满足不同项目的需求。 首先,Log4j的自定义主要体现在以下几个方面: 1. **自定义日志级别**:Log4j提供了DEBUG、INFO、WARN、ERROR和FATAL等默认的日志级别,但...

    log4j-1.2.17

    在Log4j 1.2.17中,你可以设置不同的日志级别,如DEBUG、INFO、WARN、ERROR和FATAL,以便根据需要过滤不同级别的日志信息。这使得开发者可以在开发和生产环境中灵活调整日志输出,避免过多的无关日志影响系统性能。 ...

    log4j-java

    4. **数据库(Database)**:将日志写入数据库,便于结构化查询和分析。 5. **Email**:发送日志信息到指定邮箱,便于及时获取错误信息。 6. **自定义Appender**:开发者可以根据需求实现自己的日志输出策略。 ### ...

    log4j 1.2.8 jar 包含源码

    在 Log4j 中,日志级别定义了日志事件的重要性,包括 `DEBUG`、`INFO`、`WARN`、`ERROR` 和 `FATAL`。这些级别允许开发者根据需要过滤和记录不同严重程度的信息,以优化性能和管理日志输出。 **2. Appenders** ...

    既简单又实用的log4j知识(.doc)

    6. **在代码中使用Log4j**:在需要记录日志的类中,使用`LogFactory.getLog`获取一个`Log`实例,然后调用`debug`、`info`、`warn`、`error`、`fatal`等方法记录不同级别的日志。 **理解Log4j.properties配置** `...

    Log4jToWriterAppender.zip

    本文将通过分析名为"Log4jToWriterAppender.zip"的项目,探讨如何利用Log4j中的WriterAppender来改变日志的输出目的地,以满足特定的日志处理需求。 WriterAppender是Log4j的一个组件,它允许我们将日志信息写入到...

    log4j-2.5-src

    Apache Log4j 是Java领域中广泛使用的日志记录框架,尤其在2.5版本中,它提供了高效、灵活的日志处理能力,是许多企业级应用的首选。本文将深入探讨Log4j 2.5的源码,理解其核心设计理念,并分享如何利用源码进行...

    Log4J日志管理类使用详解

    **Log4J日志管理类使用详解** 在Java开发中,日志记录是不可或缺的一部分,它可以帮助开发者追踪程序运行过程中的...在实际项目中,结合使用标签"源码"和"工具",可以进一步挖掘Log4J的潜力,为软件开发带来诸多便利。

    Log4j中文手册

    Log4j的设计目标是使日志记录变得简单,同时允许在复杂环境中进行高度定制,以满足不同层次的需求。 **日志级别** Log4j定义了多个日志级别,包括TRACE、DEBUG、INFO、WARN、ERROR、FATAL和OFF。这些级别按照严重...

Global site tag (gtag.js) - Google Analytics