专栏首页后端开发随笔Log4j 1.x JDBCAppender记录日志失效问题详解

Log4j 1.x JDBCAppender记录日志失效问题详解

官网:http://logging.apache.org/log4j/1.2/manual.html

事件: 最近在项目中使用log4j 1.x JDBCAppender记录管理员操作日志到数据库,在测试时发现系统启动后运行一段时间无法继续记录相关操作日志到数据库。 配置如下: log4j.properties:

log4j.logger.oplog=INFO, oplog
log4j.appender.oplog=com.lenovo.moc.portal.dao.LogJDBCAppender
log4j.appender.oplog.driver=com.mysql.jdbc.Driver
log4j.appender.oplog.URL=jdbc:mysql://192.168.2.164:3306/oplog?characterEncoding=utf8
log4j.appender.oplog.user=xxx
log4j.appender.oplog.password=xxx
log4j.appender.oplog.sql=insert into operation_loginfo (staff_id, staff_name, user_role, op_type, op_alias, create_time, content, content_alias) values ('%x{login_staff_id}', '%x{login_staff_name}','%x{login_user_role}', '%x{op_type}', '%x{op_alias}', '%d{yyyy-mm-dd hh:mm:ss}','%m', '%x{content_alias}')
log4j.appender.oplog.layout=org.apache.log4j.PatternLayout

java代码:

public class OperationLogService {
  private static final Logger logger = Logger.getLogger(OperationLogService.class);
  private static ExecutorService threadPool = Executors.newFixedThreadPool(3);;

  private static ExecutorService getThreadPool() {
    return threadPool;
  }

  /**
  * 记录操作日志
  * @param login_staff_id 员工id
  * @param login_staff_name 员工姓名
  * @param login_user_role 员工角色
  * @param op_type 操作类型
  * @param op_alias 操作别名
  * @param content_alias 操作内容
  * @param msg 附加信息
  */
  public static void log(String login_staff_id, String login_staff_name, String login_user_role, String op_type,
    String op_alias, String content_alias, final String msg) {
    getThreadPool().execute(new Runnable() {
      @Override
      public void run() {
        MDC.put("login_staff_id", login_staff_id);
        MDC.put("login_staff_name", login_staff_name);
        MDC.put("login_user_role", login_user_role);
        MDC.put("op_type", op_type);
        MDC.put("op_alias", op_alias);
        MDC.put("content_alias", content_alias);
       logger.info(msg);
      }
    });
  }

  public static void main(String[] args) {
    log("1", "zhangsan", "admin", "add_user", "添加用户", "zhangsan添加用户", "test msg");
  }
}

解决办法: 通过查看log4j 1.x JDBCAppender源码发现,并没有对数据库连接的有效性进行判断。即:一旦数据库连接断开,就无法继续写入日志。 故而,通过扩展JDBCAppender的方式,进行数据库连接重连处理:

/**
* 自定义实现Log4j日志组件,将日志记录到数据库<br />.
* 解决问题: 原生组件在系统运行过程中可能会出现数据库连接断开,导致无法正常记录日志信息到数据库.
*
* @desc com.lenovo.moc.portal.dao.LogJDBCAppender
* @author chench9@lenovo.com
* @date 2017年3月15日
*/
public class LogJDBCAppender extends JDBCAppender {
  private static final Logger logger = Logger.getLogger(LogJDBCAppender.class);

  @Override
  protected Connection getConnection() throws SQLException {
    Connection connection = super.getConnection();
    if(connection == null || connection.isClosed()) {
      logger.warn(String.format("reconnect log jdbc appender connection"));
      connection = reconnect();
    }
    return connection;
  }

  /**
  * 重新创建数据库连接
  * @return
  * @throws SQLException
  */
  private Connection reconnect() throws SQLException {
    Connection connection = DriverManager.getConnection(databaseURL, databaseUser,databasePassword);
    return connection;
  }

  /**
  * 重载父类方法,打印错误信息到日志文件 <br />
  * 同时,处理数据库重连并在出错时重试记录日志信息.
  */
  @Override
  protected void execute(String sql) throws SQLException {
    try {
      super.execute(sql);
    } catch (Exception e) {
      logger.error(String.format("log jdbc appender execute sql eror: %s", getSql()), e);
      closeConnectionInterval();
      super.execute(sql);
    }
  }

  // 真正地关闭数据库连接
  private void closeConnectionInterval() {
    if(connection == null) {
      return;
    }

    try {
      connection.close();
    } catch (SQLException e) {
      e.printStackTrace();
    } finally {
      connection = null;
    }
  }
}

log4j 1.x org.apache.log4j.jdbc.JDBCAppender类图:

org.apache.log4j.jdbc.JDBCAppender数据库连接实现:

log4j 2.x org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender类图:

显然,在log4j 2.x中,使用了数据库连接池,所以建议使用log4j 2.x版本的JdbcAppender。

【参考】 http://stackoverflow.com/questions/3880521/reconnect-to-db-within-log4j Reconnect to DB within log4j

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 细说tomcat之session持久化探秘

    业务场景: 通常,我们会在会话级别存放一些参数,期望在session生命周期内,可以一直取得保存在session中的指定数据;而只要session过期或者失效...

    2Simple
  • 细说java之编码

    JAVA编程中涉及的编码 1.J2EE中涉及的编码 (1) HttpServletResponse响应内容编码:

    2Simple
  • Elasticsearch入门之从零开始安装ik分词器

    需要在ES中使用聚合进行统计分析,但是聚合字段值为中文,ES的默认分词器对于中文支持非常不友好:会把完整的中文词语拆分为一系列独立的汉字进行聚合,显然这并不是我...

    2Simple
  • Java 基础(六)——集合源码解析 Queue

    Queue继承自 Collection,我们先来看看类结构吧,代码量比较少,我直接贴代码了。

    蜻蜓队长
  • Vue项目自动转换 px 为 rem,高保真还原设计图

    因为rem单位是相对于根节点的字体大小的,所以通过设置根节点的字体大小可以动态的改变rem的大小。

    小蔚
  • 学界 | FAIR等机构联合提出IntPhys:你的智能系统的物理知识,比得上婴儿吗?

    选自arXiv 机器之心编译 参与:Nurhachu Null、刘晓坤 婴儿和许多动物对物体的相互作用有直观理解,并能逐步掌握物体恒常性、因果关系、重力、形状不...

    机器之心
  • SSL证书的区别和申请办法

    从2017年开始意味着浏览器迁移HTTPS的重要开始,因为Chrome 56版本讲HTTP标记为非安全的网站。证书是用于SSL安全通信信道鉴权,它...

    mariolu
  • 视觉格式化模型-控制框

    我们经常用到块元素、行内元素的概念,那么,到底什么是块元素,什么是行内元素,它们有什么特点,怎么形成的,有什么作用呢?什么是块框,什么又是行内框呢? 一、块级元...

    练小习
  • 网站漏洞扫描工具 WAScan-Web Application Scanner

    WAScan是一款开源工具,该工具采用的是基于黑盒的漏洞挖掘方法,这也就意味着研究人员无需对Web应用程序的源代码进行研究,它可以直接被当作成一种模糊测试工具来...

    周俊辉
  • 在公司内网如何更新IntelliJ的插件

    最近小伙伴们更新IntelliJ后,发现没法安装或者更新插件了,每次尝试在线安装时总会提示SSL错误。特别是要玩Scala的小伙伴更是抓狂,因为本身Intell...

    Allen Cheng

扫码关注云+社区

领取腾讯云代金券