自定义数据库字段日志存储 log4jdbc-log4j2+logback

自定义日志存储 log4jdbc-log4j2+logback(包括sql存储)

自定义MyDBAppender
前提配置好log4jdbc.log4j2.properties和logback.xml文件
创建好表:
表略
"INSERT INTO " +
" log(px_name,info,log_status,s_sql,sql_count,start_time,end_time) "+
“VALUES (?,?,?,?,?,?,?)”
详细内容请看 注释:

///**
// * Logback: the reliable, generic, fast and flexible logging framework.
// * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
// *
// * This program and the accompanying materials are dual-licensed under
// * either the terms of the Eclipse Public License v1.0 as published by
// * the Eclipse Foundation
// *
// *   or (per the licensee's choosing)
// *
// * under the terms of the GNU Lesser General Public License version 2.1
// * as published by the Free Software Foundation.
// */
package com.weipu.dx45gdata.common.config;
//
//import static ch.qos.logback.core.db.DBHelper.closeStatement;
//
//import java.lang.reflect.Method;
//import java.sql.Connection;
//import java.sql.PreparedStatement;
//import java.sql.SQLException;
//import java.sql.Timestamp;
//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.ColumnName;
//import ch.qos.logback.classic.db.names.DBNameResolver;
//import ch.qos.logback.classic.db.names.DefaultDBNameResolver;
//import ch.qos.logback.classic.db.names.TableName;
//import ch.qos.logback.classic.spi.*;
//import ch.qos.logback.core.CoreConstants;

import ch.qos.logback.classic.db.names.ColumnName;
import ch.qos.logback.classic.db.names.DBNameResolver;
import ch.qos.logback.classic.db.names.DefaultDBNameResolver;
import ch.qos.logback.classic.db.names.TableName;
import ch.qos.logback.classic.spi.*;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.db.DBAppenderBase;
import lombok.extern.slf4j.Slf4j;

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

/**
 * The DBAppender inserts logging events into three database tables in a format
 * independent of the Java programming language.
 *
 * For more information about this appender, please refer to the online manual
 * at http://logback.qos.ch/manual/appenders.html#DBAppender
 *
 * @author Ceki Gülcü
 * @author Ray DeCampo
 * @author Sébastien Pennec
 */
@Slf4j
public class MyDBAppender extends DBAppenderBase<ILoggingEvent> {
//    protected String insertPropertiesSQL; 自带的表插入语句
//    protected String insertExceptionSQL自带的表插入语句
    protected String insertSQL; //自定义表插入语句
    protected static final Method GET_GENERATED_KEYS_METHOD;

    private DBNameResolver dbNameResolver;
	//该数字顺序是作用在PrepareStatment上
    static final int PX_NAME_INDEX=1;
    static final int  INFO_INDEX=2;
    static final int  STATUS_INDEX=3;
    static final int SQL_INDEX=4;
    static final int SQL_COUNT_INDEX=5;
    static final int START_TIME_INDEX=6;
    static final int END_TIME_INDEX=7;
    static final int ARG0_INDEX = 5;
    static final int CALLER_FILENAME_INDEX = 6;
    static final int CALLER_CLASS_INDEX = 7;
    static final int CALLER_METHOD_INDEX = 8;
    static final int CALLER_LINE_INDEX = 9;
    static final int EVENT_ID_INDEX = 10;

    static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();

    static {
        // PreparedStatement.getGeneratedKeys() method was added in JDK 1.4
        Method getGeneratedKeysMethod;
        try {
            // the
            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;
    }

    @Override
    //启动的地方,相当于初始化的地方
    public void start() {
        if (dbNameResolver == null)
            dbNameResolver = new DefaultDBNameResolver();
//        insertExceptionSQL = buildInsertExceptionSQL(dbNameResolver);
//        insertPropertiesSQL = buildInsertPropertiesSQL(dbNameResolver);
		//获取到插入语句
        insertSQL = buildInsertSQL(dbNameResolver);
        super.start();
    }

    @Override
    //该类工作的地方,表示初始化完成,执行的第一个工作方法
    //原理是在父类的public void append(E eventObject) 方法里
    protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {

		
        String info = bindLoggingEventWithInsertStatement(insertStatement, event);
        if(info.isEmpty()){
            return;
        }
        int updateCount = insertStatement.executeUpdate();
        if (updateCount != 1) {
            addWarn("Failed to insert loggingEvent");
        }
    }
 //该类工作的地方,表示初始化完成,执行的第二个工作方法
     protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {
     
// 若需要把错误sql语句记录在日志中,并把正确sql剔除,请看这里。
//原理:sql信息和sql错误信息是连续的,也就是eventId连续,当插入sql信息后,如果下一次是Error信息并且为"jdbc.sqlonly",就会删除之前插入sql的信息。
if(event.getLevel().levelStr.equals("ERROR")&&event.getLoggerName().equals("jdbc.sqlonly")){
//            PreparedStatement preparedStatement = connection.prepareStatement("DELETE FROM log WHERE event_id = ?");
//            preparedStatement.setLong(1,eventId-1);
//            int i = preparedStatement.executeUpdate( );
//            if(i!=1){
//                addWarn("Failed to Delete loggingEvent");
//            };
//
//        }

//        Map<String, String> mergedMap = mergePropertyMaps(event);
//        insertProperties(mergedMap, connection, eventId);
//
//        if (event.getThrowableProxy() != null) {
//            insertThrowable(event.getThrowableProxy(), connection, eventId);
//
//        }
    }

    String bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {
        String px_name="";
        String info="";
        String status="";
        String sql="";
        Integer sql_count=0;
        Timestamp start_time = null;
        Timestamp end_time=null;

        //px_name
        px_name=event.getMDCPropertyMap().get("px_name");
        //status
        status=event.getLevel( ).levelStr.equals("ERROR")?"failed":"success";
        //info sql
        if(event.getLoggerName().equals("jdbc.sqlonly")){
        	
            //记录jdbc.sqlonly ERROR级别信息  event.getThrowableProxy()不为空
            //jdbc.sqlonly错误信息存在代理异常。
            if(event.getThrowableProxy()!=null){
                IThrowableProxy throwableProxy = event.getThrowableProxy( );
                info=throwableProxy.getClassName()+": "+throwableProxy.getMessage();
                sql = event.getMessage( ).substring(event.getMessage( ).indexOf("I"));
            }
        }
        else{
            // 主动抛出异常不是代理异常
            if(event.getThrowableProxy()!=null){
            	//基本不会触发
                info=ThrowableProxyUtil.asString(event.getThrowableProxy( )).substring(0,255);
            }else{
                //拦截了
                info=event.getMessage();
            }
        }
        //sql_count
        if (event.getMDCPropertyMap().get("sql_count")!=null){
            sql_count= Integer.valueOf(event.getMDCPropertyMap().get("sql_count"));
        }
        //start_time
        if(event.getMDCPropertyMap().get("start_time")!=null){
            start_time=new Timestamp(Long.valueOf(event.getMDCPropertyMap().get("start_time")));
        }

        //end_time
        if(event.getMDCPropertyMap().get("end_time")!=null){
            end_time=new Timestamp(Long.valueOf(event.getMDCPropertyMap().get("end_time")));
        }

        stmt.setString(PX_NAME_INDEX,px_name);
        stmt.setString(INFO_INDEX,info);
        stmt.setString(STATUS_INDEX,status);
        stmt.setString(SQL_INDEX,sql);
        stmt.setInt(SQL_COUNT_INDEX,sql_count);
        stmt.setTimestamp(START_TIME_INDEX,start_time);
        stmt.setTimestamp(END_TIME_INDEX,end_time);
        return info;

    }
    //自带表后尾字段处理的 不需要
    
    @Override
    protected Method getGeneratedKeysMethod() {
        return GET_GENERATED_KEYS_METHOD;
    }

    @Override
    protected String getInsertSQL() {
        return insertSQL;
    }
	//自定义表插入sql
    static String buildInsertSQL(DBNameResolver dbNameResolver) {
        StringBuilder sqlBuilder = new StringBuilder(
                "INSERT INTO " +
                        " log(px_name,info,log_status,s_sql,sql_count,start_time,end_time) "+
                        "VALUES (?,?,?,?,?,?,?)"
        );
        return sqlBuilder.toString();
    }
}





以下是自定义类的父类:
核心在这个方法里了

 @Override
    public void append(E eventObject) {
        Connection connection = null;
        PreparedStatement insertStatement = null;
        try {
            connection = connectionSource.getConnection();
            connection.setAutoCommit(false);

            if (cnxSupportsGetGeneratedKeys) {
                String EVENT_ID_COL_NAME = "EVENT_ID";
                // see
                if (connectionSource.getSQLDialectCode() == SQLDialectCode.POSTGRES_DIALECT) {
                    EVENT_ID_COL_NAME = EVENT_ID_COL_NAME.toLowerCase();
                }
                insertStatement = connection.prepareStatement(getInsertSQL(), new String[] { EVENT_ID_COL_NAME });
            } else {
                insertStatement = connection.prepareStatement(getInsertSQL());
            }

            long eventId;
            // inserting an event and getting the result must be exclusive
            synchronized (this) {
                subAppend(eventObject, connection, insertStatement);
                eventId = selectEventId(insertStatement, connection);
            }
            secondarySubAppend(eventObject, connection, eventId);

            connection.commit();
        } catch (Throwable sqle) {
            addError("problem appending event", sqle);
        } finally {
            DBHelper.closeStatement(insertStatement);
            DBHelper.closeConnection(connection);
        }
    }

总结:
该类的主要内容包括相关字段(列顺序,插入语句)、方法(启动方法、工作方法1、工作方法2、获取插入语句方法、为PrepareStatment放入参数方法)。
注意:
无法异步,由于父类的synchronized 作用域没有包含connection.commit(),在提交之前Connection的一些内容可能会被覆盖和清空,可能会出现莫名其妙的情况。
留言:
有写不到位的地方还请大佬指正,顺便能给出异步问题的解决思路就更好了。
(_)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值