步骤一:添加pom依赖
<!-- 自定义日志
这个依赖必须存在,否则会报java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
步骤二:配置logback.xml
注意:里面标签配置顺序别弄错了
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--log存放路径-->
<property name="LOG_HOME" value="/my-log" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{address} %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/${APP_NAME}.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>10</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{address} %-5level %logger{50} - %msg%n</pattern>
<!--<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern> -->
</encoder>
</appender>
<!--自定义日志记录输出需添加数据库配置
class:LogDBAppender日志保存操作类(自定义)
-->
<appender name="db_classic_mysql_pool" class="com.*.config.LogDBAppender">
<connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
<dataSource class="org.apache.commons.dbcp.BasicDataSource">
<driverClassName>com.mysql.jdbc.Driver</driverClassName>
<url>jdbc:mysql://*.*.*.*:3306/mydb?serverTimezone=GMT%2B8&characterEncoding=utf8</url>
<username>*</username>
<password>*</password>
</dataSource>
</connectionSource>
</appender>
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<!--引入上面数据库配置-->
<appender-ref ref="db_classic_mysql_pool" />
</root>
</configuration>
步骤三: 配置自定义日志类
import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.db.DBAppenderBase;
import com.alibaba.fastjson.JSONObject;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
/**
* @ClassName LogDBAppender
* @Description: 自定义日志保存至数据库
* @Author LRH
* @Date 2020/6/15
* @Version V1.0
**/
@Configuration
public class LogDBAppender extends DBAppenderBase<ILoggingEvent> {
protected static final Method GET_GENERATED_KEYS_METHOD;
//插入sql
protected String insertSQL;
//自定义存储字段
/**
* menu_type:操作类型,指的是菜单ID
* record_id:记录ID
* operation_content:操作内容,自定义编辑
* */
static final int MENU_TYPE = 1;
static final int RECORD_ID = 2;
static final int OPERATION_CONTENT = 3;
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() {
// 将写好的sql语句赋值给insertSQL
insertSQL = buildInsertSQL();
super.start();
}
// 自己写新增sql语句
//在数据库中创建一个log表,专门存日志数据
private static String buildInsertSQL() {
return "INSERT INTO `log`" +
"(" +
"`menu_type`,`record_id`,`operation_content`"+
")" +
"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 {
// event.getFormattedMessage() 日志打印内容
String message = event.getFormattedMessage();
// logger.info("'MENU_TYPE': '{}','RECORD_ID': '{}','OPERATION_CONTENT': '{}'",XXX,XXX,XXX)
// 如果只想存储上面格式的日志,则可以判断打印数据是否符合要求
//判断当前日志信息是否属于自定义类型
int MENU_TYPE_FLAG=message.indexOf("MENU_TYPE");
int RECORD_ID_FLAG=message.indexOf("RECORD_ID");
int OPERATION_CONTENT_FLAG=message.indexOf("OPERATION_CONTENT");
if(MENU_TYPE_FLAG>0&&RECORD_ID_FLAG>0&&OPERATION_CONTENT_FLAG>0){
//截取用户自定义的日志信息
JSONObject jsonObject =JSONObject.parseObject(message);
String menuType=jsonObject.get("MENU_TYPE").toString();
String recordId=jsonObject.get("RECORD_ID").toString();
String operationContent=jsonObject.get("OPERATION_CONTENT").toString();
stmt.setString(MENU_TYPE, menuType);
stmt.setString(RECORD_ID, recordId);
stmt.setString(OPERATION_CONTENT, operationContent);
}
}
@Override
protected void subAppend(ILoggingEvent eventObject, Connection connection, PreparedStatement statement) throws Throwable {
bindLoggingEventWithInsertStatement(statement, eventObject);
// This is expensive... should we do it every time?
int updateCount = statement.executeUpdate();
if (updateCount != 1) {
addWarn("Failed to insert loggingEvent");
}
}
@Override
protected void secondarySubAppend(ILoggingEvent eventObject, Connection connection, long eventId) throws Throwable {
}
}
步骤四:如何调用执行自定义日志
private static Logger logger = LoggerFactory.getLogger(WxAuthController.class);
//日志消息模板(属性自定义)
public final static String INFO="{\"MENU_TYPE\":\"{}\",\"RECORD_ID\":\"{}\",\"OPERATION_CONTENT\":\"{}\"}";
//调用方式如下(当前配置的日志级别是info)
logger.info(INFO,参数1,参数2,参数3)