logback.xml配置文件:
复制代码 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n ${LOG_HOME}/%d{yyyy-MM-dd}/MIXPAY_%d{yyyy-MM-s}.log 50 %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n 50MB DB ACCEPT DENY net.sf.log4jdbc.DriverSpy jdbc:log4jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=UTF-8 root 123456 true
异步日志的核心配置如下:
复制代码 DB ACCEPT DENY net.sf.log4jdbc.DriverSpy jdbc:log4jdbc:mysql://127.0.0.1:3306/dbname?characterEncoding=UTF-8 root 123456
自定义 LogDBAppender (Appender是logback框架中最重要的组件之一)
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.CallerData;import ch.qos.logback.classic.spi.ILoggingEvent;import ch.qos.logback.core.db.DBAppenderBase; 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; /** * @Title: LogDBAppender.java * @Description: TODO(日志持久化配置) * @Author: 爱飘de小子 上午9:43 */public class LogDBAppender extends DBAppenderBase{ 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 { // 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(); insertSQL = buildInsertSQL(dbNameResolver); super.start(); } @Override protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable { bindLoggingEventWithInsertStatement(insertStatement, event); bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray()); // 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 event, Connection connection, long eventId) throws Throwable { Map 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(TIMESTMP_INDEX, event.getTimeStamp()); stmt.setString(FORMATTED_MESSAGE_INDEX, event.getFormattedMessage()); stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName()); stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString()); stmt.setString(THREAD_NAME_INDEX, event.getThreadName()); stmt.setShort(REFERENCE_FLAG_INDEX, DBHelper.computeReferenceMask(event)); } 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(ARG0_INDEX + i, asStringTruncatedTo254(argArray[i])); } if (arrayLen < 4) { for (int i = arrayLen; i < 4; i++) { stmt.setString(ARG0_INDEX + 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; } else { return s.substring(0, 254); } } void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException { StackTraceElement caller = extractFirstCaller(callerDataArray); stmt.setString(CALLER_FILENAME_INDEX, caller.getFileName()); stmt.setString(CALLER_CLASS_INDEX, caller.getClassName()); stmt.setString(CALLER_METHOD_INDEX, caller.getMethodName()); stmt.setString(CALLER_LINE_INDEX, 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 mergePropertyMaps(ILoggingEvent event) { Map mergedMap = new HashMap (); // we add the context properties first, then the event properties, since // we consider that event-specific properties should have priority over // context-wide properties. Map loggerContextMap = event.getLoggerContextVO().getPropertyMap(); Map mdcMap = event.getMDCPropertyMap(); if (loggerContextMap != null) { mergedMap.putAll(loggerContextMap); } if (mdcMap != null) { mergedMap.putAll(mdcMap); } return mergedMap; } @Override protected Method getGeneratedKeysMethod() { return GET_GENERATED_KEYS_METHOD; } @Override protected String getInsertSQL() { return insertSQL; } 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(); } }复制代码
日志拦截 新建LogbackMarkerFilter类
import ch.qos.logback.classic.spi.ILoggingEvent;import ch.qos.logback.core.filter.AbstractMatcherFilter;import ch.qos.logback.core.spi.FilterReply;import org.slf4j.Marker;import org.slf4j.MarkerFactory; /** * @Title: LogbackMarkerFilter.java * @Description: TODO(日志拦截) * @Author: 爱飘de小子 上午10:15 */public class LogbackMarkerFilter extends AbstractMatcherFilter{ private Marker markerToMatch = null; @Override public void start() { if (null != this.markerToMatch) { super.start(); } else { addError(" no MARKER yet !"); } } @Override public FilterReply decide(ILoggingEvent event) { Marker marker = event.getMarker(); if (!isStarted()) { return FilterReply.NEUTRAL; } if (null == marker) { return onMismatch; } if (markerToMatch.contains(marker)) { return onMatch; } return onMismatch; } public void setMarker(String markerStr) { if (null != markerStr) { markerToMatch = MarkerFactory.getMarker(markerStr); } } }复制代码
数据库脚本:
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表,logging_event_property和logging_event_exception可以删除。
使用:
//指定配置的Marker log.info(MarkerFactory.getMarker("DB"),"hello,logback!");复制代码
数据库显示如下
如果项目集成spring-data-jpa,可以不运行数据库脚本,配置开启自动更新表,并新建LoggingEvent类:
import lombok.Data;import javax.persistence.*; /** * @Title: LoggingEvent.java * @Description: TODO(日志持久实体) * @Author: 爱飘de小子 上午10:57 */@Data@Entity@Table(name="logging_event")public class LoggingEvent { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @Column(name = "event_id", nullable = false, columnDefinition = "BIGINT UNSIGNED") private Long eventId; @Column(name = "timestmp", nullable = false, columnDefinition = "BIGINT") private Long timestmp; @Column(name = "formatted_message", nullable = false, columnDefinition = "text") private String formattedMessage; @Column(name = "logger_name", nullable = false, columnDefinition = "varchar(254)") private String loggerName; @Column(name = "level_string", nullable = false, columnDefinition = "varchar(254)") private String levelString; @Column(name = "thread_name", columnDefinition = "varchar(254) DEFAULT NULL") private String threadName; @Column(name = "reference_flag", columnDefinition = "smallint(6)") private Integer referenceFlag; @Column(name = "arg0", columnDefinition = "varchar(254) DEFAULT NULL") private String arg0; @Column(name = "arg1", columnDefinition = "varchar(254) DEFAULT NULL") private String arg1; @Column(name = "arg2", columnDefinition = "varchar(254) DEFAULT NULL") private String arg2; @Column(name = "arg3", columnDefinition = "varchar(254) DEFAULT NULL") private String arg3; @Column(name = "caller_filename", nullable = false, columnDefinition = "varchar(254)") private String callerFilename; @Column(name = "caller_class", nullable = false, columnDefinition = "varchar(254)") private String callerClass; @Column(name = "caller_method", nullable = false, columnDefinition = "varchar(254)") private String callerMethod; @Column(name = "caller_line", nullable = false, columnDefinition = "varchar(5)") private String callerLine; }复制代码