1.使用方式
和正常的log.info一样使用,根据规定好的格式判定日志采集
2.日志拦截
实现AuditLogAppender类,获取日志信息进行处理。原理是继承AppenderBase类,可以拦截info打印的日志,然后判断是否审计日志,处理日志信息。
package com.dehua.aop;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import com.alibaba.fastjson.JSON;
import com.dehua.entity.AuditLogDO;
import com.dehua.util.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 自定义日志采集
*
* @param <E>
*/
@Slf4j
@Component
public class AuditLogAppender<E> extends AppenderBase<E> implements ApplicationContextAware {
private static AuditLogService auditLogService;
@Override
protected void append(E eventObject) {
try {
// 获取日志
if (eventObject instanceof ILoggingEvent) {
ILoggingEvent event = (ILoggingEvent) eventObject;
String message = event.getMessage();
if (!Objects.equals(message, CommonConstants.AUDIT_LOG_INFO)) {
return;
}
Object[] argumentArray = event.getArgumentArray();
String methodName = "unknown";
String className = "unknown";
StackTraceElement[] callerDataArray = event.getCallerData();
if (null != callerDataArray && callerDataArray.length > 0) {
StackTraceElement callerData = callerDataArray[0];
methodName = callerData.getMethodName();
className = callerData.getClassName();
}
String auditName = argumentArray[0]+"";
String auditNo = argumentArray[1]+"";
String auditType = argumentArray[2]+"";
Object paramJson = argumentArray[3];
Object resultJson = argumentArray[4];
AuditLogDO auditLogDO = AuditLogFactory.buildAuditLogDO(auditName, auditNo, auditType,
className, methodName, JSON.toJSONString(paramJson), JSON.toJSONString(resultJson));
//log.info(JSON.toJSONString(auditLogDO));
//简单处理,插入数据库
auditLogService.save(auditLogDO);
}
} catch (Exception e) {
log.error("审计日志采集异常 e:", e);
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
auditLogService = applicationContext.getAutowireCapableBeanFactory().getBean(AuditLogService.class);
}
}
日志工厂类AuditLogFactory
package com.dehua.aop;
import com.alibaba.fastjson.JSON;
import com.dehua.entity.AuditLogDO;
import com.dehua.util.CommonJoinPointUtil;
import com.dehua.util.HttpServletUtil;
import lombok.extern.slf4j.Slf4j;
import ognl.Ognl;
import ognl.OgnlContext;
import ognl.OgnlException;
import org.aspectj.lang.JoinPoint;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Map;
@Slf4j
public class AuditLogFactory {
private AuditLogFactory() {
}
public static AuditLogDO buildAuditLogDO(JoinPoint joinPoint, Object result, AuditLog commonLog) {
String auditName = commonLog.name();
String idOgnl = commonLog.idOgnl();
String auditType = commonLog.type();
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
Map<String, Object> paramMap = CommonJoinPointUtil.getMethodArgsAsMap(joinPoint);
String auditNo = getAuditNo(idOgnl, paramMap);
return buildAuditLogDO(auditName, auditNo, auditType, className, methodName,
JSON.toJSONString(paramMap), JSON.toJSONString(result));
}
public static AuditLogDO buildAuditLogDO(String auditName, String auditNo, String auditType, String className,
String methodName, String paramJson, String resultJson) {
//HttpServletRequest request = HttpServletUtil.getRequest();
String requestURI = "url";//request.getRequestURI();
String traceId = "traceId";//TraceIdUtils.getTraceId();
AuditLogDO auditLoDO = new AuditLogDO();
auditLoDO.setAuditName(auditName);
auditLoDO.setAuditNo(auditNo);
auditLoDO.setAuditType(auditType);
auditLoDO.setReqUrl(requestURI);
auditLoDO.setClassName(className);
auditLoDO.setMethodName(methodName);
auditLoDO.setParamJson(paramJson);
auditLoDO.setResultJson(resultJson);
auditLoDO.setTraceId(traceId);
auditLoDO.setCreateDate(LocalDateTime.now());
auditLoDO.setCreateUser("");
return auditLoDO;
}
public static String getAuditNo(String idOgnl, Map<String, Object> paramMap) {
OgnlContext context = new OgnlContext();
context.setRoot(paramMap);
Object root = context.getRoot();
try {
return String.valueOf(Ognl.getValue(idOgnl, context, root));
} catch (OgnlException e) {
log.error("Ognl key:{} error:", idOgnl, e);
}
return idOgnl;
}
}
常量类,约定审计日志的格式
public class CommonConstants {
/**
* 审计日志前缀
*/
public static final String AUDIT_LOG_PREFIX = "audit_log:";
/**
* 审计日志:auditName auditNo auditType paramJson resultJson
*/
public static final String AUDIT_LOG_INFO = AUDIT_LOG_PREFIX + "{} {} {} {} {}";
}
3.logback-spring.xml日志文件配置
resources目录下添加logback-spring.xml文件,配置审计日志<AUDIT_LOG_APPENDER>
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 彩色日志格式 -->
<property name="log.pattern" value="%magenta(%d{yyyy-MM-dd HH:mm:ss.SSS}) %cyan([TRACE_ID:%X{X-TraceId}]) %yellow([Call_Ip:%X{X-Call-Ip}]) %blue([%thread]) %highlight(%-5level) %green(%logger) - %msg%n" />
<property name="log.file" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${PID} %logger{39}: %msg%n" />
<!-- Console 输出设置 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- 输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>info</level>
</filter>
<encoder>
<pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0}%X{ServiceId} -%X{trace-id} %m%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- info日志 appender -->
<appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>${log.file}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter"><!-- 只打印info日志 -->
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="AUDIT_LOG_APPENDER" class="com.dehua.aop.AuditLogAppender">
<!-- 审计日志 -->
</appender>
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="INFO"/>
<appender-ref ref="AUDIT_LOG_APPENDER"/>
<appender-ref ref="STDOUT"/>
</root>
</configuration>
增加的配置如下,就可以实现审计日志拦截
4控制层
package com.dehua.controller;
import com.dehua.aop.AuditLog;
import com.dehua.entity.UserDO;
import com.dehua.util.CommonConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("test/user/")
public class UserController {
@AuditLog(name = "操作用户", idOgnl = "userDO.name", type = "add")
@PostMapping("add")
public String add(@RequestBody UserDO userDO) {
log.info("添加用户");
log.info(CommonConstants.AUDIT_LOG_INFO,"操作用户-info",userDO.getName(),"add",userDO,"ok");
return "ok";
}
@AuditLog(name = "操作用户", idOgnl = "userDO.id", type = "update")
@PostMapping("update")
public String update(UserDO userDO) {
log.info("修改用户");
return "ok";
}
}