• AOP与Filter拦截请求打印日志实用例子


    相信各位同道在写代码的时候,肯定会写一些日志打印,因为这对往后的运维而言,至关重要的。

    那么我们请求一个restfull接口的时候,哪些信息是应该被日志记录的呢?

    以下做了一个基本的简单例子,这里只是示例说明基本常规实现记录的信息,根据项目的真实情况选用 : 

    1 . Http请求拦截器(Filter) : 从 HttpServletRequest获取基本的请求信息

    复制代码
    package name.ealen.config;
    
    import name.ealen.util.CommonUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.util.UUID;
    
    /**
     * Created by EalenXie on 2018/9/7 15:56.
     * Http请求拦截器,日志打印请求相关信息
     */
    @Configuration
    public class FilterConfiguration {
    
        private static final Logger log = LoggerFactory.getLogger(FilterConfig.class);
    
        @Bean
        @Order(Integer.MIN_VALUE)
        @Qualifier("filterRegistration")
        public FilterRegistrationBean filterRegistration() {
            FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
            registration.setFilter(controllerFilter());
            registration.addUrlPatterns("/*");
            return registration;
        }
    
        private Filter controllerFilter() {
            return new Filter() {
                @Override
                public void init(FilterConfig filterConfig) {
                    log.info("ControllerFilter init Success");
                }
    
                @Override
                public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
                    HttpServletRequest request = (HttpServletRequest) servletRequest;
                    HttpServletResponse response = (HttpServletResponse) servletResponse;
                    String requestId = request.getHeader("Request-Id");
                    if (requestId == null) requestId = request.getRequestedSessionId();
                    if (requestId == null) requestId = UUID.randomUUID().toString();
                    if (!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
                        System.out.println();
                        log.info("Http Request Request-Id : " + requestId);
                        log.info("Http Request Information : {"URI":"" + request.getRequestURL() +
                                "","RequestMethod":"" + request.getMethod() +
                                "","ClientIp":"" + CommonUtil.getIpAddress(request) +
                                "","Content-Type":"" + request.getContentType() +
                                "","UserAgent":"" + request.getHeader("user-agent") +
                                ""}");
                    }
                    chain.doFilter(request, response);
                }
    
                @Override
                public void destroy() {
                    log.info("ControllerFilter destroy");
                }
            };
        }
    }
    复制代码

    2 . Controller的拦截AOP : 获取 请求的对象,请求参数,返回数据,请求返回状态,内部方法耗时。

    复制代码
    package name.ealen.config;
    
    import com.alibaba.fastjson.JSON;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.core.env.Environment;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.Resource;
    
    /**
     * Created by EalenXie on 2018/9/7 14:19.
     * AOP打印日志 : 请求的对象,请求参数,返回数据,请求状态,内部方法耗时
     */
    @Aspect
    @Component
    public class ControllerInterceptor {
    
        private static final Logger log = LoggerFactory.getLogger(ControllerInterceptor.class);
    
        @Resource
        private Environment environment;
    
        private String getAppName() {
            try {
                return environment.getProperty("spring.application.name");
            } catch (Exception ignore) {
                return "unnamed";
            }
        }
    
        /**
         * 注意 : pjp.proceed()执行的异常请务必抛出,交由ControllerAdvice捕捉到并处理
         */
        @Around(value = "execution (*  name.ealen.web.*.*(..))")
        public Object processApiFacade(ProceedingJoinPoint pjp) throws Throwable {
            long startTime = System.currentTimeMillis();
            String name = pjp.getTarget().getClass().getSimpleName();
            String method = pjp.getSignature().getName();
            Object result;
            try {
                result = pjp.proceed();
                log.info("RequestTarget : " + getAppName() + "." + name + "." + method);
                Object[] requestParams = pjp.getArgs();
                if (requestParams.length > 0) {     //日志打印请求参数
                    try {
                        log.info("RequestParam : {}", JSON.toJSON(requestParams));
                    } catch (Exception e) {
                        for (Object param : requestParams) {
                            try {
                                log.info("RequestParam : {}", JSON.toJSON(param));
                            } catch (Exception ig) {
                                log.info("RequestParam : {}", param.toString());
                            }
                        }
                    }
                }
            } finally {
                log.info("Internal Method Cost Time: {}ms", System.currentTimeMillis() - startTime);
            }
            return result;
        }
    
    }
    复制代码

    3 . 全局的异常处理返回Advice :  

    复制代码
    package name.ealen.config;
    
    import com.alibaba.fastjson.JSON;
    import name.ealen.util.CommonUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.client.HttpClientErrorException;
    import org.springframework.web.client.HttpServerErrorException;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Created by EalenXie on 2018/11/8 16:25.
     * 全局异常、错误返回处理
     */
    @ControllerAdvice
    public class ControllerExceptionListener {
    
        private final Logger log = LoggerFactory.getLogger(ControllerExceptionListener.class);
    
        @ExceptionHandler(value = Throwable.class)
        public ResponseEntity Throwable(Throwable throwable, HttpServletRequest request) {
            Map<String, String> resultMap = getThrowable(throwable);
            if (request != null) {
                Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
                resultMap.put("Requester-Ip", CommonUtil.getIpAddress(request));
                resultMap.put("Requester-Agent", request.getHeader("user-agent"));
                if (statusCode != null) {
                    new ResponseEntity<>(JSON.toJSON(resultMap).toString(), HttpStatus.valueOf(statusCode));
                }
            }
            return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    
        @ExceptionHandler(value = HttpServerErrorException.class)
        public ResponseEntity HttpServerErrorException(HttpServerErrorException serverError) {
            Map<String, String> resultMap = getThrowable(serverError);
            HttpStatus status = serverError.getStatusCode();
            resultMap.put("responseBody", "" + serverError.getResponseBodyAsString());
            resultMap.put("statusCode", "" + status.toString());
            resultMap.put("statusText", "" + serverError.getStatusText());
            resultMap.put("statusReasonPhrase", "" + status.getReasonPhrase());
            return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), status);
        }
    
        @ExceptionHandler(value = HttpClientErrorException.class)
        public ResponseEntity HttpClientErrorException(HttpClientErrorException clientError) {
            Map<String, String> resultMap = getThrowable(clientError);
            HttpStatus status = clientError.getStatusCode();
            resultMap.put("responseBody", "" + clientError.getResponseBodyAsString());
            resultMap.put("statusCode", "" + clientError.getStatusCode().toString());
            resultMap.put("statusText", "" + clientError.getStatusText());
            resultMap.put("statusReasonPhrase", "" + status.getReasonPhrase());
            return new ResponseEntity<>(JSON.toJSON(resultMap).toString(), status);
        }
    
    
        /**
         * 公共异常信息
         */
        private Map<String, String> getThrowable(Throwable throwable) {
            Map<String, String> resultMap = new HashMap<>();
            resultMap.put("throwable", "" + throwable);
            resultMap.put("throwableTime", "" + CommonUtil.getCurrentDateTime());
            resultMap.put("message", "" + throwable.getMessage());
            resultMap.put("localizedMessage", "" + throwable.getLocalizedMessage());
            log.error("Exception : {}", JSON.toJSON(resultMap));
            throwable.printStackTrace();
            return resultMap;
        }
    }
    复制代码

    4 . 提供一个简单的restfull接口 : 

    复制代码
    package name.ealen.web;
    
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * Created by EalenXie on 2018/9/7 14:24.
     */
    @RestController
    public class SayHelloController {
    
        @RequestMapping("/sayHello")
        public String sayHello() {
            return "hello world";
        }
    
        @RequestMapping("/say")
        public ResponseEntity<?> say(@RequestBody Object o) {
            return new ResponseEntity<>(o, HttpStatus.OK);
        }
    
    }
    复制代码

    4 . 使用Postman进行基本测试 : 

    5 . 控制台可以看到基本效果 : 

     

     

     以上只是关于Controller应该记录日志的一个简单的例子,完整代码可见 https://github.com/EalenXie/springboot-controller-logger

    感谢各位提出意见和支持。

  • 相关阅读:
    每日一题20180325
    Linux下MySQL表名区分大小写
    CentOS删除编译安装的Python3
    HTTPS配置
    测试 js 方法运行时间(转)
    使用dbutils进行批处理
    oracle生成主键
    JDBC学习笔记(10)——调用函数&存储过程(转)
    easyui Draggable
    blob
  • 原文地址:https://www.cnblogs.com/huangjinyong/p/11211996.html
Copyright © 2020-2023  润新知