网站访问记录日志能方便的帮助我们开发人员准确的定位到问题,能帮助我们进行错误重现,快速的解决问题,节省时间。这里我将项目中用到的两种记录方式简单总结一下,希望能帮助有需要的人
本文代码需要对Spring拦截器、AOP有一定的了解,可以先百度了解下Spring拦截器、AOP的概念及用途
首先创建拦截器LoggerFilter
,继承HandlerInterceptorAdapter
类
package com.os.common.intercepter;
import com.os.core.utils.web.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Enumeration;
/**
* 访问日志
*
* @author Peng
*/
public class LoggerFilter extends HandlerInterceptorAdapter {
private static Logger logger = LoggerFactory.getLogger(LoggerFilter.class);
public LoggerFilter() {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
super.afterCompletion(request, response, handler, ex);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
super.postHandle(request, response, handler, modelAndView);
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取用户登录ip
String ip = this.getIpAddr(request);
String path = request.getContextPath() + request.getServletPath();
StringBuilder params = new StringBuilder();
String key;
String[] values;
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
key = (String) parameterNames.nextElement();
values = request.getParameterValues(key);
params.append(key).append("=").append(Arrays.toString(values)).append("&");
}
if (params.length() > 0) {
params.deleteCharAt(params.length() - 1);
}
logger.info("访问者信息:ip地址=" + ip + ",访问地址=" + path + ",参数:(" + params.toString().replaceAll("[\\[\\]]", "") + ")");
return true;
}
/**
* 获取请求IP地址
*
* @param request request请求
* @return ip地址
*/
private String getIpAddr(HttpServletRequest request) {
String ipAddress;
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException var4) {
var4.printStackTrace();
}
ipAddress = ObjectUtils.isNotNull(inet) ? inet.getHostAddress() : "";
}
}
if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
return ipAddress;
}
}
HandlerInterceptorAdapter
中afterCompletion
、postHandle
、preHandle
这三个方法就不介绍作用了,直接百度java拦截器就行了
现在将LoggerFilter
添加到springmvc配置中
本文代码配置方法采用的是javaconfig方式,先贴出代码
/**
* 拦截器配置
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//日志记录拦截器
registry.addInterceptor(new LoggerFilter())
// 拦截请求
.addPathPatterns("/**/*")
// 剔除静态文件访问
.excludePathPatterns("/static/**/*");
super.addInterceptors(registry);
}
这样就OK了
xml方式配置
<bean id="handlerInterceptor"
class="com.os.common.intercepter.LoggerFilter"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<bean class="com.os.common.intercepter.LoggerFilter">bean>
mvc:interceptor>
mvc:interceptors>
第一种方式就介绍完了,需要注意的是拦截器配置顺序,如果只是记录日志的话,建议将代码写在最上面,保证拦截器一定执行
因为项目只需记录能访问进方法的日志,所以对不能访问的日志就不需要记录
项目采用注解的方式来记录日志,就是说如果只有controller中方法使用了注解才会记录
所以先创建自定义注解Log
/**
* 自定义日志注解
*
* @author Peng
*/
@Documented
@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}
然后创建LogAspect
切面
package com.os.common.aspect;
import com.os.core.utils.json.FastJsonUtils;
import com.os.core.utils.web.ObjectUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
/**
* 日志aop切面
*
* @author Peng
*/
@Configuration
// 开启aop 相当于 "true"/>
@EnableAspectJAutoProxy
// 切面
@Aspect
// 把普通pojo实例化到spring容器中,相当于配置文件中的"" class=""/>
@Component
public class LogAspect {
/**
* 日志输出
*/
private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);
/**
* 日志记录AOP
* 使用 @Around环绕通知,切点使用@annotation(xxxxx)进行定义
* 即 使用@Log自定义注解的方法进入此方法
*/
@Around("@annotation(com.os.common.aspect.Log)")
public Object aroundCacheAble(ProceedingJoinPoint joinPoint) throws Throwable {
try {
long startTime = System.currentTimeMillis();
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String method = request.getMethod();
String uri = request.getRequestURI();
String ip = this.getIpAddr(request);
StringBuilder params = new StringBuilder();
String key;
String[] values;
Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
key = (String) parameterNames.nextElement();
values = request.getParameterValues(key);
params.append(key).append("=").append(Arrays.toString(values)).append("&");
}
if (params.length() > 0) {
params.deleteCharAt(params.length() - 1);
}
UUID uuid = UUID.randomUUID();
String id = uuid.toString().replaceAll("-", "");
// 获取token
logger.info("↓↓↓↓↓↓↓↓↓ id:" + id + ",访问地址=" + uri + ",ip地址=" + ip + ",参数:(" + params.toString().replaceAll("[\\[\\]]", "") + "),提交方式:" + method);
// 环绕通知=前置+目标方法执行+后置通知,proceed方法就是用于启动目标方法执行的
Object result = joinPoint.proceed();
logger.info("返回数据" + FastJsonUtils.toJSONString(result));
return result;
} catch (Exception e) {
// 异常捕获
// 获取类型、方法名
String className = joinPoint.getTarget().getClass().getSimpleName();
String methodName = joinPoint.getSignature().getName();
String errInfo = className + "." + methodName + "--error";
// 异常日志输出
LoggerFactory.getLogger(joinPoint.getTarget().getClass()).error(errInfo, e);
if (isJson(joinPoint)) {
// 返回错误json
Map json = new HashMap<>(4);
json.put("message", "这是json错误页");
return json;
} else {
// 返回错误页面
return "pages/error";
}
}
}
/**
* 获取请求IP地址
*
* @param request request请求
* @return ip地址
*/
private String getIpAddr(HttpServletRequest request) {
String ipAddress;
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException var4) {
var4.printStackTrace();
}
ipAddress = ObjectUtils.isNotNull(inet) ? inet.getHostAddress() : "";
}
}
if (ipAddress != null && ipAddress.length() > 15 && ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
return ipAddress;
}
/**
* 判断是否是ajax返回
*
* @param joinPoint 连接点
* @return true ajax请求 false 页面请求
*/
private Boolean isJson(ProceedingJoinPoint joinPoint) {
Class> classTarget = joinPoint.getTarget().getClass();
Class>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
try {
// 带@ResponseBody注册的方法为ajax方法,所以判断是否带@ResponseBody即可
Method objMethod = classTarget.getMethod(joinPoint.getSignature().getName(), par);
ResponseBody annotation = objMethod.getAnnotation(ResponseBody.class);
if (annotation != null) {
return true;
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return false;
}
}
至此,两种方式就总结完了,本人倾向于第二种,可以使用aop环绕通知方法统计出代码耗时等,当然拦截器也可以做出这种效果就是麻烦些。