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

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

posted @ 2019-07-19 10:59  黄进广寒  阅读(1180)  评论(0编辑  收藏  举报