一、需求
我们本节要实现的需求
- 针对当前系统的每一次接口访问,要记录是什么人访问的(用户名)、什么时间访问的、访问耗时多长时间、使用什么HTTP method方法访问的、访问结果如何等。可以称为审计日志。
- 将访问记录审计日志,输出到一个单独的日志文件access.log
二、定义访问日志内容记录实体类
@Data
public class AccessLog {
private String username;
private String url;
private Integer duration;
private String httpMethod;
private Integer httpStatus;
private String ip;
private Date createTime;
}
三、自定义日志拦截器
通过自定义拦截器的方式,记录审计日志。
- 拦截器的preHandle方法,可以用于拦截请求处理开始。用于记录请求开始时间等信息保存到Http Request,用于后续计算请求时长。
- 拦截器的postHandle方法,可以用于拦截请求处理完成。可以从Request对象获取开始时间,计算本次请求总的处理时长等信息。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
public class AccessLogInterceptor implements HandlerInterceptor {
private static final String LOGGER_SEND_TIME = "SEND_TIME";
private static final String LOGGER_ACCESSLOG = "ACCESSLOG_ENTITY";
private static final Logger logger = LoggerFactory.getLogger("ACCESS-LOG");
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
AccessLog accessLog = new AccessLog();
accessLog.setIp(AdrressIpUtils.getIpAdrress(request));
accessLog.setHttpMethod(request.getMethod());
accessLog.setUrl(request.getRequestURI());
request.setAttribute(LOGGER_SEND_TIME,System.currentTimeMillis());
request.setAttribute(LOGGER_ACCESSLOG,accessLog);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) throws Exception {
AccessLog accessLog = (AccessLog) request.getAttribute(LOGGER_ACCESSLOG);
int status = response.getStatus();
accessLog.setHttpStatus(status);
accessLog.setUsername("admin");
long currentTime = System.currentTimeMillis();
long snedTime = Long.valueOf(request.getAttribute(LOGGER_SEND_TIME).toString());
accessLog.setDuration(Integer.valueOf((currentTime - snedTime)+""));
accessLog.setCreateTime(new Date());
logger.info(accessLog.toString());
}
}
上文中LoggerFactory.getLogger("ACCESS-LOG") 获取一个日志配置中的Logger的名字,用于打印日志输出,持久化到日志文件里面。"ACCESS-LOG"的日志Logger定义我们下文中介绍。
四、拦截器注册
拦截器注册就不细讲了,前面的章节已经介绍过,回看。
@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
private final String[] excludePath = {"/static"};
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new AccessLogInterceptor()).addPathPatterns("/**").excludePathPatterns(excludePath);
}
}
五、获取ip访问地址的工具类
上面的代码中涉及到一个工具类,用于获取请求客户端的ip。代码如下:
public class AdrressIpUtils {
public static String getIpAdrress(HttpServletRequest request) {
String Xip = request.getHeader("X-Real-IP");
String XFor = request.getHeader("X-Forwarded-For");
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
int index = XFor.indexOf(",");
if(index != -1){
return XFor.substring(0,index);
}else{
return XFor;
}
}
XFor = Xip;
if(StringUtils.isNotEmpty(XFor) && !"unKnown".equalsIgnoreCase(XFor)){
return XFor;
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isBlank(XFor) || "unknown".equalsIgnoreCase(XFor)) {
XFor = request.getRemoteAddr();
}
return XFor;
}
}
六、"ACCESS-LOG"的日志Logger定义
下文中的配置参考以Log4J2配置为例(参考上一节的内容进行学习) [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y0r1Nn7P-1644633419822)(images/screenshot_1598061650386.png)]
LoggerFactory.getLogger("ACCESS-LOG") 代码去配置文件里面找一个name为ACCESS-LOG的Logger配置。- 该Logger是一个AsyncLogger,指向的输出目标是ACCESS-APPENDER
- ACCESS-APPENDER是一个日志文件输出配置,日志文件是access-log.log
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<properties>
<property name="LOG_HOME">D:/logs</property>
</properties>
<Appenders>
<RollingFile name="ACCESS-APPENDER"
fileName="${LOG_HOME}/access.log"
filePattern="${LOG_HOME}/access-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<pattern>[%d][%p][%t][%C] %m%n</pattern>
</PatternLayout>
<Policies>
<SizeBasedTriggeringPolicy size="100MB"/>
<TimeBasedTriggeringPolicy/>
</Policies>
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</Appenders>
<Loggers>
<AsyncLogger name="ACCESS-LOG" level="debug" additivity="false">
<AppenderRef ref="ACCESS-APPENDER" level="info"/>
</AsyncLogger>
</Loggers>
</configuration>
|