logback日志自定义DBAppender

该文详细介绍了logback-spring-prod.xml配置文件的内容,包括日志级别设定、控制台输出格式、日志异步到数据库的实现,以及如何避免读取不到yaml属性的问题。同时,展示了自定义的DBAppender如何捕获并存储异常信息到数据库。
摘要由CSDN通过智能技术生成
  1. 日志配置文件logback-spring-prod.xml 日志配置文件名称尽量避免默认的,否则有可能读取不到yaml的属性

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
     当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">

    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>

    <!--1. 输出到控制台-->
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <encoder>
            <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
            <!-- 设置字符集 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <springProperty scope="context" name="serverName" source="logging.serverName"/>
    <springProperty scope="context" name="post" source="logging.post"/>
    <springProperty scope="context" name="dbName" source="logging.dbName"/>
    <springProperty scope="context" name="userName" source="logging.userName"/>
    <springProperty scope="context" name="password" source="logging.password"/>
    <springProperty scope="context" name="logLevel" source="logging.logLevel"/>
    <!--日志异步到数据库  -->
    <appender name="dbAppender" class="com.xx.common.extension.CustomDBAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>${logLevel}</level>
        </filter>
        <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
            <dataSource class="org.apache.commons.dbcp.BasicDataSource">
                <driverClassName>com.mysql.cj.jdbc.Driver</driverClassName>
                <url>jdbc:mysql://${serverName}:${post}/${dbName}?useUnicode=true;characterEncoding=UTF-8;useSSL=false;serverTimezone=Asia/Shanghai</url>
                <username>${userName}</username>
                <password>${password}</password>
            </dataSource>
        </connectionSource>
    </appender>

    <springProperty scope="context" name="loggerName" source="logging.logName"/>


    <!-- root的默认level是DEBUG -->
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="dbAppender"/>
    </root>

</configuration>
  1. yaml配置

logging:
  config: classpath:logback-spring-prod.xml
  serverName: xx.xx.xx.xx
  post: 3306
  dbName: dbName
  userName: userName
  password: password
  logLevel: ERROR
  1. CustomDBAppender 复制DBAppender的代码,根据自己的需求修改  


public class CustomDBAppender extends DBAppenderBase<ILoggingEvent> {

    protected String insertExceptionSQL;
    protected String insertSQL;
    protected static final Method GET_GENERATED_KEYS_METHOD;
    private DBNameResolver dbNameResolver;

    static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();

    public CustomDBAppender() {
    }

    public void setDbNameResolver(DBNameResolver dbNameResolver) {
        this.dbNameResolver = dbNameResolver;
    }

    @Override
    public void start() {
        if (this.dbNameResolver == null) {
            this.dbNameResolver = new DefaultDBNameResolver();
        }

        this.insertExceptionSQL = SQLBuilder.buildInsertExceptionSQL(this.dbNameResolver);

        this.insertSQL = SQLBuilder.buildInsertSQL(this.dbNameResolver);
        super.start();
    }

    @Override
    protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
        this.bindLoggingEventWithInsertStatement(insertStatement, event);
        this.bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray());
        StackTraceElement[] stackTraceElements = event.getCallerData();
        if (event.getThrowableProxy() != null) {
            if (event.getThrowableProxy().getCause()!=null){
                StackTraceElementProxy[] stackTraceElementProxies =  event.getThrowableProxy().getCause().getStackTraceElementProxyArray();
                if (stackTraceElementProxies!=null&&stackTraceElementProxies.length>0) {
                    for (StackTraceElementProxy item : stackTraceElementProxies
                    ) {
                        if (item.getStackTraceElement().getClassName().indexOf("com.xx") >= 0 && item.getStackTraceElement().getLineNumber() > 0) {
                            stackTraceElements[0] = item.getStackTraceElement();
                            break;
                        }
                    }
                }
            }
        }
        this.bindCallerDataWithPreparedStatement(insertStatement, stackTraceElements);
        int updateCount = insertStatement.executeUpdate();
        if (updateCount != 1) {
            this.addWarn("Failed to insert loggingEvent");
        }

    }

    @Override
    protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {
        /*Map<String, String> mergedMap = this.mergePropertyMaps(event);
        this.insertProperties(mergedMap, connection, eventId);*/
        if (event.getThrowableProxy() != null) {
            this.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));
    }

    void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt, Object[] argArray) throws SQLException {
        int arrayLen = argArray != null ? argArray.length : 0;

        int i;
        for(i = 0; i < arrayLen && i < 4; ++i) {
            stmt.setString(7 + i, this.asStringTruncatedTo254(argArray[i]));
        }

        if (arrayLen < 4) {
            for(i = arrayLen; i < 4; ++i) {
                stmt.setString(7 + i, (String)null);
            }
        }

    }

    String asStringTruncatedTo254(Object o) {
        String s = null;
        if (o != null) {
            s = o.toString();
        }

        if (s == null) {
            return null;
        } else {
            return s.length() <= 254 ? s : s.substring(0, 254);
        }
    }

    void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException {
        StackTraceElement caller = this.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 (this.hasAtLeastOneNonNullElement(callerDataArray)) {
            caller = callerDataArray[0];
        }

        return caller;
    }

    private boolean hasAtLeastOneNonNullElement(StackTraceElement[] callerDataArray) {
        return callerDataArray != null && callerDataArray.length > 0 && callerDataArray[0] != null;
    }



    @Override
    protected Method getGeneratedKeysMethod() {
        return GET_GENERATED_KEYS_METHOD;
    }

    @Override
    protected String getInsertSQL() {
        return this.insertSQL;
    }



    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 {
        try{
            StringBuilder buf = new StringBuilder();
            ThrowableProxyUtil.subjoinFirstLine(buf, tp);
            this.updateExceptionStatement(insertExceptionStatement, buf.toString(), baseIndex++, eventId);
            int commonFrames = tp.getCommonFrames();
            StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();

            for(int i = 0; i < stepArray.length; ++i) {
                StringBuilder sb = new StringBuilder();
                sb.append('\t');
                ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]);
                this.updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex++, eventId);
            }


        }catch (Exception e){
            System.out.println("11");
        }
        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(true) {
                if (tp == null) {
                    if (this.cnxSupportsBatchUpdates) {
                        exceptionStatement.executeBatch();
                    }
                    break;
                }
                if (tp.getCause()!=null){
                    tp = tp.getCause();
                }
                baseIndex = this.buildExceptionStatement(tp, baseIndex, exceptionStatement, eventId);
                tp = tp.getCause();
            }
        } finally {
            ch.qos.logback.core.db.DBHelper.closeStatement(exceptionStatement);
        }

    }

    static {
        Method getGeneratedKeysMethod;
        try {
            getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[])null);
        } catch (Exception var2) {
            getGeneratedKeysMethod = null;
        }

        GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
    }
}
  1. SQLBuilder


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();
    }
}

表结构 logging_event.sql


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for logging_event
-- ----------------------------
DROP TABLE IF EXISTS `logging_event`;
CREATE TABLE `logging_event`  (
  `timestmp` bigint(20) NOT NULL,
  `formatted_message` longtext CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `logger_name` varchar(254) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `level_string` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `thread_name` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `reference_flag` smallint(6) DEFAULT NULL,
  `arg0` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `arg1` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `arg2` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `arg3` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
  `caller_filename` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `caller_class` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `caller_method` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `caller_line` char(4) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  `event_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `create_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`event_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 504 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

表结构 logging_event_exception.sql


SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for logging_event_exception
-- ----------------------------
DROP TABLE IF EXISTS `logging_event_exception`;
CREATE TABLE `logging_event_exception`  (
  `event_id` bigint(20) NOT NULL,
  `i` smallint(6) NOT NULL,
  `trace_line` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
  PRIMARY KEY (`event_id`, `i`) USING BTREE,
  CONSTRAINT `logging_event_exception_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>