网站访问日志记录的两种方式

网站访问记录日志能方便的帮助我们开发人员准确的定位到问题,能帮助我们进行错误重现,快速的解决问题,节省时间。这里我将项目中用到的两种记录方式简单总结一下,希望能帮助有需要的人

本文代码需要对Spring拦截器、AOP有一定的了解,可以先百度了解下Spring拦截器、AOP的概念及用途

一、使用Spring拦截器来记录

首先创建拦截器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;
    }
}

HandlerInterceptorAdapterafterCompletionpostHandlepreHandle这三个方法就不介绍作用了,直接百度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>  

第一种方式就介绍完了,需要注意的是拦截器配置顺序,如果只是记录日志的话,建议将代码写在最上面,保证拦截器一定执行

二、使用Spring AOP来记录

因为项目只需记录能访问进方法的日志,所以对不能访问的日志就不需要记录

项目采用注解的方式来记录日志,就是说如果只有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环绕通知方法统计出代码耗时等,当然拦截器也可以做出这种效果就是麻烦些。

你可能感兴趣的:(Spring)