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