-
日志配置文件logback-spring-prod.xml 日志配置文件名称尽量避免默认的,否则有可能读取不到yaml的属性
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wex"
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="wEx"
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<springProperty scope="context" name="serverName" source="logging.serverName"/>
<springProperty scope="context" name="post" source="logging.post"/>
<springProperty scope="context" name="dbName" source="logging.dbName"/>
<springProperty scope="context" name="userName" source="logging.userName"/>
<springProperty scope="context" name="password" source="logging.password"/>
<springProperty scope="context" name="logLevel" source="logging.logLevel"/>
<!--日志异步到数据库 -->
<appender name="dbAppender" class="com.xx.common.extension.CustomDBAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>${logLevel}</level>
</filter>
<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://${serverName}:${post}/${dbName}?useUnicode=true;characterEncoding=UTF-8;useSSL=false;serverTimezone=Asia/Shanghai</url>
<username>${userName}</username>
<password>${password}</password>
</dataSource>
</connectionSource>
</appender>
<springProperty scope="context" name="loggerName" source="logging.logName"/>
<!-- root的默认level是DEBUG -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="dbAppender"/>
</root>
</configuration>
-
yaml配置
logging:
config: classpath:logback-spring-prod.xml
serverName: xx.xx.xx.xx
post: 3306
dbName: dbName
userName: userName
password: password
logLevel: ERROR
-
CustomDBAppender 复制DBAppender的代码,根据自己的需求修改
public class CustomDBAppender extends DBAppenderBase<ILoggingEvent> {
protected String insertExceptionSQL;
protected String insertSQL;
protected static final Method GET_GENERATED_KEYS_METHOD;
private DBNameResolver dbNameResolver;
static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();
public CustomDBAppender() {
}
public void setDbNameResolver(DBNameResolver dbNameResolver) {
this.dbNameResolver = dbNameResolver;
}
@Override
public void start() {
if (this.dbNameResolver == null) {
this.dbNameResolver = new DefaultDBNameResolver();
}
this.insertExceptionSQL = SQLBuilder.buildInsertExceptionSQL(this.dbNameResolver);
this.insertSQL = SQLBuilder.buildInsertSQL(this.dbNameResolver);
super.start();
}
@Override
protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable {
this.bindLoggingEventWithInsertStatement(insertStatement, event);
this.bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray());
StackTraceElement[] stackTraceElements = event.getCallerData();
if (event.getThrowableProxy() != null) {
if (event.getThrowableProxy().getCause()!=null){
StackTraceElementProxy[] stackTraceElementProxies = event.getThrowableProxy().getCause().getStackTraceElementProxyArray();
if (stackTraceElementProxies!=null&&stackTraceElementProxies.length>0) {
for (StackTraceElementProxy item : stackTraceElementProxies
) {
if (item.getStackTraceElement().getClassName().indexOf("com.xx") >= 0 && item.getStackTraceElement().getLineNumber() > 0) {
stackTraceElements[0] = item.getStackTraceElement();
break;
}
}
}
}
}
this.bindCallerDataWithPreparedStatement(insertStatement, stackTraceElements);
int updateCount = insertStatement.executeUpdate();
if (updateCount != 1) {
this.addWarn("Failed to insert loggingEvent");
}
}
@Override
protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable {
/*Map<String, String> mergedMap = this.mergePropertyMaps(event);
this.insertProperties(mergedMap, connection, eventId);*/
if (event.getThrowableProxy() != null) {
this.insertThrowable(event.getThrowableProxy(), connection, eventId);
}
}
void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException {
stmt.setLong(1, event.getTimeStamp());
stmt.setString(2, event.getFormattedMessage());
stmt.setString(3, event.getLoggerName());
stmt.setString(4, event.getLevel().toString());
stmt.setString(5, event.getThreadName());
stmt.setShort(6, DBHelper.computeReferenceMask(event));
}
void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt, Object[] argArray) throws SQLException {
int arrayLen = argArray != null ? argArray.length : 0;
int i;
for(i = 0; i < arrayLen && i < 4; ++i) {
stmt.setString(7 + i, this.asStringTruncatedTo254(argArray[i]));
}
if (arrayLen < 4) {
for(i = arrayLen; i < 4; ++i) {
stmt.setString(7 + i, (String)null);
}
}
}
String asStringTruncatedTo254(Object o) {
String s = null;
if (o != null) {
s = o.toString();
}
if (s == null) {
return null;
} else {
return s.length() <= 254 ? s : s.substring(0, 254);
}
}
void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException {
StackTraceElement caller = this.extractFirstCaller(callerDataArray);
stmt.setString(11, caller.getFileName());
stmt.setString(12, caller.getClassName());
stmt.setString(13, caller.getMethodName());
stmt.setString(14, Integer.toString(caller.getLineNumber()));
}
private StackTraceElement extractFirstCaller(StackTraceElement[] callerDataArray) {
StackTraceElement caller = EMPTY_CALLER_DATA;
if (this.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 this.insertSQL;
}
void updateExceptionStatement(PreparedStatement exceptionStatement, String txt, short i, long eventId) throws SQLException {
exceptionStatement.setLong(1, eventId);
exceptionStatement.setShort(2, i);
exceptionStatement.setString(3, txt);
if (this.cnxSupportsBatchUpdates) {
exceptionStatement.addBatch();
} else {
exceptionStatement.execute();
}
}
short buildExceptionStatement(IThrowableProxy tp, short baseIndex, PreparedStatement insertExceptionStatement, long eventId) throws SQLException {
try{
StringBuilder buf = new StringBuilder();
ThrowableProxyUtil.subjoinFirstLine(buf, tp);
this.updateExceptionStatement(insertExceptionStatement, buf.toString(), baseIndex++, eventId);
int commonFrames = tp.getCommonFrames();
StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
for(int i = 0; i < stepArray.length; ++i) {
StringBuilder sb = new StringBuilder();
sb.append('\t');
ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]);
this.updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex++, eventId);
}
}catch (Exception e){
System.out.println("11");
}
return baseIndex;
}
protected void insertThrowable(IThrowableProxy tp, Connection connection, long eventId) throws SQLException {
PreparedStatement exceptionStatement = null;
try {
exceptionStatement = connection.prepareStatement(this.insertExceptionSQL);
short baseIndex = 0;
while(true) {
if (tp == null) {
if (this.cnxSupportsBatchUpdates) {
exceptionStatement.executeBatch();
}
break;
}
if (tp.getCause()!=null){
tp = tp.getCause();
}
baseIndex = this.buildExceptionStatement(tp, baseIndex, exceptionStatement, eventId);
tp = tp.getCause();
}
} finally {
ch.qos.logback.core.db.DBHelper.closeStatement(exceptionStatement);
}
}
static {
Method getGeneratedKeysMethod;
try {
getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[])null);
} catch (Exception var2) {
getGeneratedKeysMethod = null;
}
GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
}
}
-
SQLBuilder
public class SQLBuilder {
public SQLBuilder() {
}
static String buildInsertPropertiesSQL(DBNameResolver dbNameResolver) {
StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_PROPERTY)).append(" (");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", ");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_KEY)).append(", ");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_VALUE)).append(") ");
sqlBuilder.append("VALUES (?, ?, ?)");
return sqlBuilder.toString();
}
static String buildInsertExceptionSQL(DBNameResolver dbNameResolver) {
StringBuilder sqlBuilder = new StringBuilder("INSERT INTO ");
sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_EXCEPTION)).append(" (");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", ");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.I)).append(", ");
sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TRACE_LINE)).append(") ");
sqlBuilder.append("VALUES (?, ?, ?)");
return sqlBuilder.toString();
}
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();
}
}
表结构 logging_event.sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for logging_event
-- ----------------------------
DROP TABLE IF EXISTS `logging_event`;
CREATE TABLE `logging_event` (
`timestmp` bigint(20) NOT NULL,
`formatted_message` longtext CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`logger_name` varchar(254) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`level_string` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`thread_name` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`reference_flag` smallint(6) DEFAULT NULL,
`arg0` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`arg1` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`arg2` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`arg3` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL,
`caller_filename` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`caller_class` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`caller_method` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`caller_line` char(4) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
`event_id` bigint(20) NOT NULL AUTO_INCREMENT,
`create_time` datetime(0) DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`event_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 504 CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
表结构 logging_event_exception.sql
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for logging_event_exception
-- ----------------------------
DROP TABLE IF EXISTS `logging_event_exception`;
CREATE TABLE `logging_event_exception` (
`event_id` bigint(20) NOT NULL,
`i` smallint(6) NOT NULL,
`trace_line` varchar(254) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL,
PRIMARY KEY (`event_id`, `i`) USING BTREE,
CONSTRAINT `logging_event_exception_ibfk_1` FOREIGN KEY (`event_id`) REFERENCES `logging_event` (`event_id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;