最近研究了一下logback,发现百度的结果太杂乱,并且还有明显胡编乱造的作者,所以自己记录一下探索的过程。
注意事项:
1:如果配置文件中要使用spring的配置,应把配置文件名称改为logback-spring.xml,并配合springProperty 标签一块使用即可。
2:appender 标签用于配置输出源(控制台,文件,数据库...),class属性中可以制定一个实现类用于处理。
3:logger 标签中name用于配置指定的包名,additivity为true标签表示该包处理完日志还会向上(root)输出。
4: 以下配置文件实现的功能: 读取spring的配置(不同的包配置不同的日志表和级别;数据库连接等信息);
把日志中的两个包下产生的日志,存到数据库的不同的表中,所有的日志都打印在控制台一份。
例如 org.flowable 存到 WF_LOG表中
5: 附 logback-spring.xml 和 FlowableLogForMysqlAppender.java 和创建WF_LOG表的sql文件
6:配置自定义日志输出主要就是这三个文件,其中的标签和方法名及其作用,多仔细看看代码和配置,就能搞明白很简单。
7:源码路径:( logback-classic jar包下 )
创建表的sql: ch.qos.logback.classic.db.script 文件下有不同库的建表语句(新版本的jar好像放在resources 下)
相关处理类: ch.qos.logback.classic.db.DBAppender 和 ch.qos.logback.classic.db.SQLBuilder
myApplicationName
${driver-class-name}
${url}
${username}
${password}
${driver-class-name}
${url}
${username}
${password}
%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %msg - %file:%line%n
UTF-8
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level - %msg%n
package com.lzmispt.config;
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.sql.Timestamp;
/**
* @Description: 配置flowable日志写入mysql的 WF_Log
* @Author: ldy
* @Date: 2020-07-08 16:22
*/
public class FlowableLogForMysqlAppender extends DBAppenderBase{
private String insertSQL;
private static final Method GET_GENERATED_KEYS_METHOD;
private static final int TIME_INDEX = 1;
private static final int MESSAGE_INDEX = 2;
private static final int LEVEL_STRING_INDEX = 3;
private static final int LOGGER_NAME_INDEX = 4;
private static final int THREAD_NAME_INDEX = 5;
private static final int CALLER_FILENAME_INDEX = 6;
private static final int CALLER_CLASS_INDEX = 7;
private static final int CALLER_METHOD_INDEX = 8;
private static final int CALLER_LINE_INDEX = 9;
private 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 = buildInsertSQL();
super.start();
}
private static String buildInsertSQL() {
return "INSERT INTO WF_Log " +
"(time, message, logger_name, level_string, thread_name," +
"caller_filename, caller_class, caller_method, caller_line)"+
"VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?)";
}
private void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {
stmt.setTimestamp(TIME_INDEX, new Timestamp(event.getTimeStamp()));
stmt.setString(MESSAGE_INDEX, event.getFormattedMessage());
stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString());
stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName());
stmt.setString(THREAD_NAME_INDEX, event.getThreadName());
}
private 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()));
}
@Override
protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
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 WF_Log");
}
}
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;
}
@Override
protected Method getGeneratedKeysMethod() {
return GET_GENERATED_KEYS_METHOD;
}
@Override
protected String getInsertSQL() {
return insertSQL;
}
protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {
}
}
BEGIN;
DROP TABLE IF EXISTS WF_Log;
COMMIT;
BEGIN;
CREATE TABLE WF_Log
(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
time DATETIME(6) NOT NULL,
message TEXT NOT NULL,
level_string VARCHAR(254) NOT NULL,
logger_name VARCHAR(254) NOT NULL,
thread_name VARCHAR(254),
reference_flag SMALLINT,
caller_filename VARCHAR(254) NOT NULL,
caller_class VARCHAR(254) NOT NULL,
caller_method VARCHAR(254) NOT NULL,
caller_line CHAR(4) NOT NULL
)comment '记录flowable流程引擎的日志';
COMMIT;