Logback写入MySQL的源码扩展

    Logback是一个开源日志组件,为取代log4j而生。
    Logback主要分为三个模块:logback-core,logback-classic和logback-access。
    Logback有着许多独特而实用的特性,如Marker、参数化记录语句、条件化堆栈跟踪和强大的事件过滤等。在此不详述,有兴趣的朋友可以查看官方文档。
    入正题,在很多实际项目开发中,往往会有将日志写入自定义的用户表的需求,或者需要记录特殊的字段。这两者,原理类似。今天给大家分享下基于logback将日志写入Mysql数据库(写入其他数据库或文件同理)的一个字段扩展。
    Logback记录日志主要用到三张固定的表:logging_event,logging_event_property,logging_event_exception。结构如下: 

BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;

BEGIN;
CREATE TABLE logging_event 
  (
    timestmp         BIGINT NOT NULL,
    formatted_message  TEXT NOT NULL,
    logger_name       VARCHAR(254) NOT NULL,
    level_string      VARCHAR(254) NOT NULL,
    thread_name       VARCHAR(254),
    reference_flag    SMALLINT,
    arg0              VARCHAR(254),
    arg1              VARCHAR(254),
    arg2              VARCHAR(254),
    arg3              VARCHAR(254),
    caller_filename   VARCHAR(254) NOT NULL,
    caller_class      VARCHAR(254) NOT NULL,
    caller_method     VARCHAR(254) NOT NULL,
    caller_line       CHAR(4) NOT NULL,
    event_id          BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
  );
COMMIT;

BEGIN;
CREATE TABLE logging_event_property
  (
    event_id       BIGINT NOT NULL,
    mapped_key        VARCHAR(254) NOT NULL,
    mapped_value      TEXT,
    PRIMARY KEY(event_id, mapped_key),
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
  );
COMMIT;

BEGIN;
CREATE TABLE logging_event_exception
  (
    event_id         BIGINT NOT NULL,
    i                SMALLINT NOT NULL,
    trace_line       VARCHAR(254) NOT NULL,
    PRIMARY KEY(event_id, i),
    FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
  );
COMMIT;

    细心的读者很容易发现logging_event的设计者提供了4个(arg0,arg1,arg2,arg3)扩展字段,本文就是实现往arg0字段写入内容,写入自定义的表同理。


1.新建测试的java工程,导入相应jar包。项目目录结构如下:

2.修改logback的配置文件logback.xml,引用我们自己编写的Appender类。以下为简化的配置文件,有兴趣的读者可以查阅详细配置文档。

<?xml version="1.0" encoding="UTF-8" ?>

<configuration>
    <appender name="DB" class="com.ldl.cn.MyAppender">
        <connectionSource
            class="ch.qos.logback.core.db.DriverManagerConnectionSource">
            <driverClass>com.mysql.jdbc.Driver</driverClass>
            <url>jdbc:mysql://localhost:3306/logback</url>
            <user>*******</user>
            <password>********</password>
        </connectionSource>
    </appender>

    <root level="ALL">
        <appender-ref ref="DB" />
    </root>
</configuration>

3.编写Appender类和SQLBuilder类。

Appender类源码:

package com.ldl.cn;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import ch.qos.logback.classic.db.DBHelper;
import ch.qos.logback.classic.db.names.DBNameResolver;
import ch.qos.logback.classic.db.names.DefaultDBNameResolver;
import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.db.DBAppenderBase;

public class MyAppender extends DBAppenderBase<ILoggingEvent>{

       public MyAppender() {}
    
       protected String insertPropertiesSQL;
       protected String insertExceptionSQL;
       protected String insertSQL;
       protected static final Method GET_GENERATED_KEYS_METHOD;
       private DBNameResolver dbNameResolver;
       static final int TIMESTMP_INDEX = 1;
       static final int FORMATTED_MESSAGE_INDEX = 2;
       static final int LOGGER_NAME_INDEX = 3;
       static final int LEVEL_STRING_INDEX = 4;
       static final int THREAD_NAME_INDEX = 5;
       static final int REFERENCE_FLAG_INDEX = 6;
       static final int ARG0_INDEX = 7;
       static final int ARG1_INDEX = 8;
       static final int ARG2_INDEX = 9;
       static final int ARG3_INDEX = 10;
       static final int CALLER_FILENAME_INDEX = 11;
       static final int CALLER_CLASS_INDEX = 12;
       static final int CALLER_METHOD_INDEX = 13;
       static final int CALLER_LINE_INDEX = 14;
       static final int EVENT_ID_INDEX = 15;
       static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();
       
          static
          {
            Method getGeneratedKeysMethod;
            try
            {
             getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[])null);
            }
            catch (Exception ex) {
              getGeneratedKeysMethod = null;
            }
            GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
          }
          
          public void setDbNameResolver(DBNameResolver dbNameResolver) {
              this.dbNameResolver = dbNameResolver;
          }
          
             public void start()
             {
                if (this.dbNameResolver == null)
                 this.dbNameResolver = new DefaultDBNameResolver();
                this.insertExceptionSQL = SQLBuilder.buildInsertExceptionSQL(this.dbNameResolver);
                this.insertPropertiesSQL = SQLBuilder.buildInsertPropertiesSQL(this.dbNameResolver);
                this.insertSQL = SQLBuilder.buildInsertSQL(this.dbNameResolver);
                super.start();
             }
       
             @Override
            protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement)
                    throws Throwable 
                   {
                          bindLoggingEventWithInsertStatement(insertStatement, event);
                          bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray());
                          bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
                         
                          int updateCount = insertStatement.executeUpdate();
                          if (updateCount != 1) {
                            addWarn("Failed to insert loggingEvent");
                         }
                       }
             
             
            @Override
            protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId)
                    throws Throwable {
                // TODO Auto-generated method stub
                Map<String, String> mergedMap = mergePropertyMaps(event);
                insertProperties(mergedMap, connection, eventId);
                if (event.getThrowableProxy() != null) {
                    insertThrowable(event.getThrowableProxy(), connection, eventId);
                }
            }
            
            void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException
           {
              stmt.setLong(1, event.getTimeStamp());
              stmt.setString(2, event.getFormattedMessage());
              stmt.setString(3, event.getLoggerName());
              stmt.setString(4, event.getLevel().toString());
              stmt.setString(5, event.getThreadName());
              stmt.setShort(6, DBHelper.computeReferenceMask(event));
              stmt.setString(7, "yourString");
           }
            
              void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt, Object[] argArray)
                        throws SQLException
                      {
                        int arrayLen = argArray != null ? argArray.length : 0;
                        
                        for (int i = 0; (i < arrayLen) && (i < 4); i++) {
                          stmt.setString(8 + i, asStringTruncatedTo254(argArray[i]));
                        }
                        if (arrayLen < 4) {
                          for (int i = arrayLen; i < 4; i++) {
                            stmt.setString(8 + i, null);
                          }
                        }
                      }
              
              String asStringTruncatedTo254(Object o) {
                    String s = null;
                     if (o != null) {
                       s = o.toString();
                    }
                    
                     if (s == null) {
                      return null;
                    }
                    if (s.length() <= 254) {
                      return s;
                    }
                     return s.substring(0, 254);
                  }
              
                 void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray)
                           throws SQLException
                         {
                            StackTraceElement caller = extractFirstCaller(callerDataArray);
                           
                            stmt.setString(11, caller.getFileName());
                            stmt.setString(12, caller.getClassName());
                            stmt.setString(13, caller.getMethodName());
                            stmt.setString(14, Integer.toString(caller.getLineNumber()));
                         }
                 
                 
                 private StackTraceElement extractFirstCaller(StackTraceElement[] callerDataArray) {
                        StackTraceElement caller = EMPTY_CALLER_DATA;
                        if (hasAtLeastOneNonNullElement(callerDataArray))
                          caller = callerDataArray[0];
                        return caller;
                     }
                 
                 private boolean hasAtLeastOneNonNullElement(StackTraceElement[] callerDataArray) {
                     return (callerDataArray != null) && (callerDataArray.length > 0) && (callerDataArray[0] != null);
                 }

                 Map<String, String> mergePropertyMaps(ILoggingEvent event) {
                      Map<String, String> mergedMap = new HashMap();
                     
                     Map<String, String> loggerContextMap = event.getLoggerContextVO().getPropertyMap();
                     
                      Map<String, String> mdcMap = event.getMDCPropertyMap();
                     if (loggerContextMap != null) {
                        mergedMap.putAll(loggerContextMap);
                     }
                      if (mdcMap != null) {
                        mergedMap.putAll(mdcMap);
                     }
                     
                      return mergedMap;
                   }
                 
            @Override
            protected Method getGeneratedKeysMethod() {
                // TODO Auto-generated method stub
                return GET_GENERATED_KEYS_METHOD;
            }
        
            @Override
            protected String getInsertSQL() {
                // TODO Auto-generated method stub
                return this.insertSQL;
            }
    
          protected void insertProperties(Map<String, String> mergedMap, Connection connection, long eventId) throws SQLException
          {
             Set<String> propertiesKeys = mergedMap.keySet();
             if (propertiesKeys.size() > 0) {
               PreparedStatement insertPropertiesStatement = null;
              try {
                 insertPropertiesStatement = connection.prepareStatement(this.insertPropertiesSQL);
        
                 for (String key : propertiesKeys) {
                   String value = (String)mergedMap.get(key);
                  
                  insertPropertiesStatement.setLong(1, eventId);
                   insertPropertiesStatement.setString(2, key);
                   insertPropertiesStatement.setString(3, value);
                  
                   if (this.cnxSupportsBatchUpdates) {
                     insertPropertiesStatement.addBatch();
                  } else {
                     insertPropertiesStatement.execute();
                  }
                }
                
                 if (this.cnxSupportsBatchUpdates) {
                   insertPropertiesStatement.executeBatch();
                }
              } finally {
                 ch.qos.logback.core.db.DBHelper.closeStatement(insertPropertiesStatement);
              }
            }
          }
          
                 void updateExceptionStatement(PreparedStatement exceptionStatement, String txt, short i, long eventId)
                           throws SQLException
                         {
                           exceptionStatement.setLong(1, eventId);
                            exceptionStatement.setShort(2, i);
                            exceptionStatement.setString(3, txt);
                            if (this.cnxSupportsBatchUpdates) {
                              exceptionStatement.addBatch();
                           } else {
                              exceptionStatement.execute();
                           }
                         }
          
             short buildExceptionStatement(IThrowableProxy tp, short baseIndex, PreparedStatement insertExceptionStatement, long eventId)
                       throws SQLException
                     {
                        StringBuilder buf = new StringBuilder();
                        ThrowableProxyUtil.subjoinFirstLine(buf, tp);
                        baseIndex = (short)(baseIndex + 1);updateExceptionStatement(insertExceptionStatement, buf.toString(), baseIndex, eventId);
                       
                   
                        int commonFrames = tp.getCommonFrames();
                        StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
                        for (int i = 0; i < stepArray.length - commonFrames; i++) {
                         StringBuilder sb = new StringBuilder();
                          sb.append('\t');
                          ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]);
                          baseIndex = (short)(baseIndex + 1);updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex, eventId);
                       }
                       
                        if (commonFrames > 0) {
                          StringBuilder sb = new StringBuilder();
                          sb.append('\t').append("... ").append(commonFrames).append(" common frames omitted");
                         
                          baseIndex = (short)(baseIndex + 1);updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex, eventId);
                       }
                       
                       return baseIndex;
                     }
          
             protected void insertThrowable(IThrowableProxy tp, Connection connection, long eventId)
                       throws SQLException
                     {
                        PreparedStatement exceptionStatement = null;
                       try {
                          exceptionStatement = connection.prepareStatement(this.insertExceptionSQL);
                         
                          short baseIndex = 0;
                         while (tp != null) {
                            baseIndex = buildExceptionStatement(tp, baseIndex, exceptionStatement, eventId);
                           
                            tp = tp.getCause();
                         }
                         
                          if (this.cnxSupportsBatchUpdates) {
                            exceptionStatement.executeBatch();
                         }
                       } finally {
                          ch.qos.logback.core.db.DBHelper.closeStatement(exceptionStatement);
                       }
                     }
          
}

SQLBuilder类源码(该类自带,默认为protected类型,外部调用会报错,故贴出):

package com.ldl.cn;

import ch.qos.logback.classic.db.names.ColumnName;
import ch.qos.logback.classic.db.names.DBNameResolver;
import ch.qos.logback.classic.db.names.TableName;

public class SQLBuilder
{

 public SQLBuilder()
 {
 }

 static String buildInsertPropertiesSQL(DBNameResolver dbNameResolver)
 {
     StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
     sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_PROPERTY)).append(" (");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_KEY)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_VALUE)).append(") ");
     sqlBuilder.append("VALUES (?, ?, ?)");
     return sqlBuilder.toString();
 }

 static String buildInsertExceptionSQL(DBNameResolver dbNameResolver)
 {
     StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
     sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_EXCEPTION)).append(" (");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.I)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TRACE_LINE)).append(") ");
     sqlBuilder.append("VALUES (?, ?, ?)");
     return sqlBuilder.toString();
 }

 static String buildInsertSQL(DBNameResolver dbNameResolver)
 {
     StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
     sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT)).append(" (");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TIMESTMP)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.FORMATTED_MESSAGE)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LOGGER_NAME)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LEVEL_STRING)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.THREAD_NAME)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.REFERENCE_FLAG)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG0)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG1)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG2)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG3)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_FILENAME)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_CLASS)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_METHOD)).append(", ");
     sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_LINE)).append(") ");
     sqlBuilder.append("VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
     return sqlBuilder.toString();
 }
}

4.测试

查看数据库:

OK!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值