关于springboot默认日志框架Slf4j+logback,自定义Appender问题
第一种:
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.filter.ThresholdFilter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.helpers.Transform;
import com.test.domain.ErrorLog;
import com.test.service.ErrorLogService;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Date;
@Component
public class DbErrorLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
/**
* 错误日志数据库增删改查服务
*/
@Autowired
private ErrorLogService errorLogService;
/**
* DbErrorLogAppender初始化
*/
@PostConstruct
public void init() {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
ThresholdFilter filter = new ThresholdFilter();
filter.setLevel("ERROR");
filter.setContext(context);
filter.start();
this.addFilter(filter);
this.setContext(context);
context.getLogger("ROOT").addAppender(DbErrorLogAppender.this);
super.start();
}
/**
* 错误日志拼装成实体类,写入数据库
*/
@Override
protected void append(ILoggingEvent loggingEvent) {
IThrowableProxy tp = loggingEvent.getThrowableProxy();
// ErrorLogPO数据表实体类
ErrorLog errorLog = new ErrorLog();
errorLog.setLevel(1);
errorLog.setReason("reason");
errorLog.setClassName("setClassName");
errorLog.setContent("content");
errorLog.setCreateTime(new Date(loggingEvent.getTimeStamp()));
try {
// 错误日志实体类写入数据库
errorLogService.addErrorLog(errorLog);
} catch (Exception ex) {
this.addError("上报错误日志失败:" + ex.getMessage());
}
}
}
该类会在报错之后调用,无需配置,写入数据库,直接继承UnsynchronizedAppenderBase<ILoggingEvent>。
第二种:
import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.core.db.DBAppenderBase; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; @Component public class LogDBAppender extends DBAppenderBase<ILoggingEvent> { protected static final Method GET_GENERATED_KEYS_METHOD; //插入sql protected String insertSQL; static final int LEVEL = 1; static final int REASON = 2; static final int CLASS_NAME = 3; static final int CONTENT = 4; static final int CREATE_TIME = 5; //static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance(); static { Method getGeneratedKeysMethod; try { 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 `error_log`(`level`,`reason`,`class_name`,`content`,`create_time`) VALUES (?,?,?,?,?)"; } @Override protected Method getGeneratedKeysMethod() { return GET_GENERATED_KEYS_METHOD; } @Override protected String getInsertSQL() { return insertSQL; } /** * 主要修改的方法 * * @param stmt * @param event * @throws SQLException */ private void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException { String message = event.getFormattedMessage(); Object[] argumentArray = event.getArgumentArray(); // stmt.setString(LEVELevent.getLevel().toString()); if(argumentArray.length>0){ stmt.setString(LEVEL, String.valueOf(argumentArray[0])); stmt.setString(REASON, String.valueOf(argumentArray[1])); }else{ stmt.setString(LEVEL, "未填写"); stmt.setString(REASON, "未填写"); } stmt.setString(CLASS_NAME, event.getLoggerName()); IThrowableProxy throwableProxy = event.getThrowableProxy(); if(throwableProxy!=null){ StringBuffer s = new StringBuffer(throwableProxy.getClassName()); s.append(throwableProxy.getMessage()); stmt.setString(CONTENT, s.toString()); }else{ stmt.setString(CONTENT, "未传入异常"); } stmt.setTimestamp(CREATE_TIME, new Timestamp(event.getTimeStamp())); } @Override protected void subAppend(ILoggingEvent eventObject, Connection connection, PreparedStatement statement) throws Throwable { bindLoggingEventWithInsertStatement(statement, eventObject); int updateCount = statement.executeUpdate(); if (updateCount != 1) { addWarn("Failed to insert loggingEvent"); } } @Override protected void secondarySubAppend(ILoggingEvent eventObject, Connection connection, long eventId) throws Throwable { } }
继承DBAppenderBase<ILoggingEvent>,需配置logback.xml
<configuration scan="true" scanPeriod="60 seconds" debug="false"> <property name="LOG_HOME" value="D:/test_log"/> <property name="LOG_NAME" value="testLog"/> <!--输出到控制台--> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} : %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <appender name="error_db_appender" class="com.test.LogDBAppender"> <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"> <dataSource class="org.apache.commons.dbcp.BasicDataSource"> <driverClassName>com.mysql.cj.jdbc.Driver</driverClassName> <url>jdbc:mysql://localhost:3306/test</url> <username>root</username> <password>****</password> </dataSource> </connectionSource> <!-- 只打印错误日志 --> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>error</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> </appender> <!--根节点--> <root level="INFO"> <appender-ref ref="console"/> <appender-ref ref="error_db_appender" /> </root> </configuration>