最近需求需要记录系统日志,在网上查询发现目前有两种主流方式。一种是利用AOP注解实现,一种是利用拦截器实现。
AOP实现的方式更为灵活,但需要为每一个需要记录的方法上加上注解(类似于白名单)。
我这个需求需要记录的是系统操作日志,范围更广,使用拦截器排除特定的Url会更适合(类似于黑名单)。
AOP实现系统操作日志及参考文章https://blog.csdn.net/u011521890/article/details/74990338
异常及错误日志参考我的另一篇文章https://blog.csdn.net/weixin_42456466/article/details/89672890
日志拦截器类
这里我省略了写入数据库dao层的操作,直接用logger日志打印出来。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.MethodParameter;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
public class LogInterceptor implements HandlerInterceptor {
private final Logger logger = LoggerFactory.getLogger(LogInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) {
SystemOperationLog log = null;
try {
log = LoggerUtil.getLog(httpServletRequest,(HandlerMethod) handler);
} catch (Exception e) {
logger.error(e.getLocalizedMessage());
}
httpServletRequest.setAttribute(LoggerUtil.LOG_OPERATE, log);
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView){
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) {
//返回视图时,插入操作日志
SystemOperationLog log = (SystemOperationLog) httpServletRequest.getAttribute(LoggerUtil.LOG_OPERATE);
if (log == null) {
logger.warn("日志信息为空");
} else {
HashMap logMap = new HashMap<>(8);
logMap.put("createTime",log.getCreateTime());
logMap.put("ip",log.getIp());
logMap.put("description",log.getDescription());
logMap.put("operation",log.getOperationType());
logMap.put("operator",log.getOperator());
logMap.put("result", null == httpServletRequest.getAttribute("flag"));
logger.info("执行记录系统操作日志操作{}",logMap);
}
}
}
loggUtil类
这里我获取方法的描述是通过swagger工具中的value注释获取的,自定义log类中的字段根据你的需求来更改。
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class LoggerUtil {
public static final String LOG_OPERATE = "operation";
public LoggerUtil() {
}
public static SystemOperationLog getLog(HttpServletRequest request, HandlerMethod handler) {
String description = null;
Method method = handler.getMethod();
Annotation[] declaredAnnotations = method.getDeclaredAnnotations();
for (Annotation annotation : declaredAnnotations) {
Class extends Annotation> clazz = annotation.annotationType();
if ("io.swagger.annotations.ApiOperation".equals(clazz.getName())) {
String annotationString = annotation.toString();
String responseString = annotationString.substring(annotationString.indexOf("response=class"),annotationString.length());
description = responseString.substring(responseString.indexOf("value=")+6,responseString.length()-1);
break;
}
}
SystemOperationLog log = new SystemOperationLog();
log.setIp(LoggerUtil.getClientIp(request));
log.setOperator("operator");
log.setOperatorId(request.getHeader("userId"));
log.setOperationType(handler.getMethod().getName());
log.setDescription(description);
log.getParametersMap().putAll(request.getParameterMap());
return log;
}
/**
* 获取客户端ip地址
*
* @param request
* @return
*/
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}
}
配置拦截器拦截规则
排除登录接口
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**").excludePathPatterns("/api/login");
super.addInterceptors(registry);
}
}
自定义log类
import lombok.Data;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@Data
public class SystemOperationLog {
/**
* 操作时间
*/
private String createTime = Timestamp.valueOf(LocalDateTime.now()).toString();
/**
*操作员
*/
private String operator;
/**
*操作员id
*/
private String operatorId;
/**
*客户端ip
*/
private String ip;
/**
*操作类型
*/
private String operationType;
/**
*操作描述
*/
private String description;
/**
*传入参数
*/
private Map parametersMap = new HashMap<>();
/**
*是否操作成功
*/
private Boolean flag;
}
主要是实现 HandlerInterceptor 接口,preHandle方法会在进入调用方法之前执行,afterCompletion会在调用方法结束之后执行。
获取相应的参数途径主要是通过
httpServletRequest.setAttribute("key",Object)方法传参,httpServletRequest.getAttribute(key)方法取参。
(HandlerMethod) handler;Method method = handler.getMethod();通过handler这个参数可以获取被调用的方法及其注解等信息。