创建一个自己的写入数据库的类继承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}