扩展logback日志,写入自己定义的表和字段。

创建一个自己的写入数据库的类继承DBAppenderBase

public class MyDBAppender extends DBAppenderBase {

    protected static final Method GET_GENERATED_KEYS_METHOD;

    //插入sql
    protected String insertSQL;
    //id
    static final int UUID_INDEX = 1;
    //用户名
    static final int USERNAME_INDEX = 2;
    //密码
    static final int PASSWORD_INDEX = 3;
    //应用的appkey
    static final int APPKEY_INDEX = 4;
    //请求接口的服务ip
    static final int SERVERIP_INDEX = 5;
    //请求地址
    static final int URL_INDEX = 6;
    //请求地址
    static final int REQUESTIP_INDEX = 7;
    //工程名
    static final int PROJECT_INDEX = 8;
    //类名
    static final int CLASS_INDEX = 9;
    //路径
    static final int CLASSPATH_INDEX = 10;
    //方法名
    static final int METHOD_INDEX = 11;
    //线程名
    static final int THREADNAME_INDEX = 12;
    //信息级别
    static final int MSGLEVEL_INDEX = 13;
    //日志信息
    static final int MSG_INDEX = 14;
    //创建时间
    static final int CREATEDATE_INDEX = 15;

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

    @Override
    public void start() {
        insertSQL = MySQLBuilder.buildInsertSQL();
        super.start();
    }

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

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

    @Override
    protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
        bindLoggingMyInfoWithPreparedStatement(insertStatement, event);
        bindLoggingEventWithInsertStatement(insertStatement, event);
        // This is expensive... should we do it every time?
        bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
        int updateCount = insertStatement.executeUpdate();
        if (updateCount != 1) {
            addWarn("Failed to insert loggingEvent");
        }
    }

    @Override
    protected void secondarySubAppend(ILoggingEvent eventObject, Connection connection, long eventId) throws Throwable {

    }

    //安全验证及个性化的数据
    void bindLoggingMyInfoWithPreparedStatement(PreparedStatement stmt, ILoggingEvent event)throws SQLException{
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
        String account = Base64Util.decodeBase64(request.getHeader("account"));
        String passwd = Base64Util.decodeBase64(request.getHeader("passwd"));
        String ip = Base64Util.decodeBase64(request.getHeader("serverIp"));
        String appkey = request.getHeader("appkey");
        stmt.setString(USERNAME_INDEX, account);
        stmt.setString(PASSWORD_INDEX, passwd);
        stmt.setString(SERVERIP_INDEX, ip);
        stmt.setString(APPKEY_INDEX, appkey);
        stmt.setString(URL_INDEX,request.getRequestURL().toString());
        stmt.setString(REQUESTIP_INDEX,request.getRemoteAddr());
        stmt.setString(PROJECT_INDEX, "tmall-api");
        Date day=new Date();
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        stmt.setString(CREATEDATE_INDEX,df.format(day));
    }

    void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {
        stmt.setString(UUID_INDEX, String.valueOf(UUID.randomUUID()));
        stmt.setString(MSG_INDEX, event.getFormattedMessage());
        stmt.setString(MSGLEVEL_INDEX, event.getLevel().toString());
        stmt.setString(THREADNAME_INDEX, event.getThreadName());
    }

    void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException {

        StackTraceElement caller = extractFirstCaller(callerDataArray);

        stmt.setString(CLASS_INDEX, caller.getFileName());
        stmt.setString(CLASSPATH_INDEX, caller.getClassName());
        stmt.setString(METHOD_INDEX, caller.getMethodName());
    }

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

    /* (non-Javadoc)
     * @see ch.qos.logback.core.db.DBAppenderBase#append(java.lang.Object)
     */
    @Override
    public void append(ILoggingEvent eventObject) {
        Connection connection = null;
        try {
            connection = connectionSource.getConnection();
            connection.setAutoCommit(false);
            PreparedStatement insertStatement;
            insertStatement = connection.prepareStatement(getInsertSQL());
            // inserting an event and getting the result must be exclusive
            synchronized (this) {
                subAppend(eventObject, connection, insertStatement);
            }

            // we no longer need the insertStatement
            if (insertStatement != null) {
                insertStatement.close();
            }
            connection.commit();
        } catch (Throwable sqle) {
            addError("problem appending event", sqle);
        } finally {
            DBHelper.closeConnection(connection);
        }
    }
}

项目启动时,会先进入start()方法,创建调用的sql,创建一个你自己的sql生成类和方法。

@Override
public void start() {
    insertSQL = MySQLBuilder.buildInsertSQL();
    super.start();
}
package com.bingo.config.log;

/**
 * Created by shenlu on 2018/3/20.
 */
public class MySQLBuilder {
    //生成插入日志的sql
    static String buildInsertSQL() {
        StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
        sqlBuilder.append("log").append(" (");
        sqlBuilder.append("id").append(", ");
        sqlBuilder.append("user_name").append(", ");
        sqlBuilder.append("pass_word").append(", ");
        sqlBuilder.append("app_key").append(", ");
        sqlBuilder.append("server_ip").append(", ");
        sqlBuilder.append("url").append(", ");
        sqlBuilder.append("request_ip").append(", ");
        sqlBuilder.append("project").append(", ");
        sqlBuilder.append("class").append(", ");
        sqlBuilder.append("classpath").append(", ");
        sqlBuilder.append("method").append(", ");
        sqlBuilder.append("thread_name").append(", ");
        sqlBuilder.append("msg_level").append(", ");
        sqlBuilder.append("msg").append(", ");
        sqlBuilder.append("create_date").append(") ");
        sqlBuilder.append("VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
        return sqlBuilder.toString();
    }
}

当Logger写入日志是,会进入subAppend方法,重写父类的方法。

@Override
protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
    bindLoggingMyInfoWithPreparedStatement(insertStatement, event);
    bindLoggingEventWithInsertStatement(insertStatement, event);
    // This is expensive... should we do it every time?
    bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
    int updateCount = insertStatement.executeUpdate();
    if (updateCount != 1) {
        addWarn("Failed to insert loggingEvent");
    }
}

做自己的写入数据实现。insertStatement是启动时生成的插入sql语句,然后忘语句中?按顺序和自己的需求插入数据。详细见bindLoggingMyInfoWithPreparedStatement、bindLoggingEventWithInsertStatement和bindCallerDataWithPreparedStatement三个方法。

最后进入重写的父类append方法,拿到数据库链接提交sql。

@Override
public void append(ILoggingEvent eventObject) {
    Connection connection = null;
    try {
        connection = connectionSource.getConnection();
        connection.setAutoCommit(false);
        PreparedStatement insertStatement;
        insertStatement = connection.prepareStatement(getInsertSQL());
        // inserting an event and getting the result must be exclusive
        synchronized (this) {
            subAppend(eventObject, connection, insertStatement);
        }

        // we no longer need the insertStatement
        if (insertStatement != null) {
            insertStatement.close();
        }
        connection.commit();
    } catch (Throwable sqle) {
        addError("problem appending event", sqle);
    } finally {
        DBHelper.closeConnection(connection);
    }
}

logback.xml中把appender的类改成自己写类MyDBappender

name="MYDB" class="com.bingo.config.log.MyDBAppender">
    class="ch.qos.logback.core.db.DriverManagerConnectionSource">
        com.mysql.jdbc.Driver
        ${spring.datasource.url}
        ${spring.datasource.username}
        ${spring.datasource.password}
    

你可能感兴趣的:(框架)