SpringMVC使用Filter打印请求和返回日志

简介:
  1. 关于SpringMVC 过滤器和拦截器的介绍,推荐大家一篇博文:https://www.cnblogs.com/rayallenbj/p/8484276.html 这里就不过多介绍了,博文中最后一句感觉还是很有深度的是: Filter依赖于Servlet容器,而Interceptor不依赖于Servlet容器。
业务场景:
  1. 在我们日常开发工作中基本框架结构 SSM,当系统出现BUG时需要后端程序员去确认问题,但是很多时候代码逻辑看不出什么问题,更多的时候是由于请求传参不同而导致的问题,因此需要开发者前期在 Controller 中打印一些有必要的日志供以后问题定位,但是因为每个人的书写习惯不同如何做到统一的打印,并且避免遗漏打印那,此时 Filter 就展现出它的作用了.
代码设计:
  1. web.xml 中加入Filter配置:
<filter>
        <description>访问日志过滤器</description>
        <filter-name>logFilter</filter-name>
        <filter-class>com.lot.springmvc.config.LogFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>logFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  1. LogFilter 中重点代码是第 40行代码,用于执行 SpringMVC中的业务
  2. LogFilter.java 代码:
/**
 * 继承 OncePerRequestFilter 保证该过滤器只会被执行一次
 *
 * @author Zhangyong
 * @date 2018/1/10
 */
public class LogFilter extends OncePerRequestFilter {

    /**
     * 初始化
     */
    public LogFilter() {
        System.out.println("init LogFilter");
    }

    /**
     *  连接器方法
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        // 请求地址
        String uri = request.getRequestURI();
        // 请求的参数
        String requestParam = JSONObject.toJSONString(request.getParameterMap());
        // 请求Body
        String bodyString = getBodyString(request);
        // 请求开始时间
        long begin = System.currentTimeMillis();
        // 打印请求信息
        System.err.println("##### Request-api   Url:" + uri + " Request:  " +requestParam + " Body :"+ bodyString);
        ResponseWrapper responseWrapper = new ResponseWrapper(response);
        // 重要:执行 SpringMVC 中 HandlerInterceptor 和 Controller
        filterChain.doFilter(request, responseWrapper);
        // 返回的结果
        String content = responseWrapper.getTextContent();
        // 打印返回结果 和 请求耗时
        System.err.println("##### Response-api  Times " + (System.currentTimeMillis()-begin) + " ms, Url: "+ uri+ " Response: " + content);
        response.getOutputStream().write(content.getBytes());

    }

    /**
     * 获取 HttpServletRequest 中 Body 参数
     * @param request
     * @return
     */
    private String getBodyString(HttpServletRequest request){
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        try{
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null){
                sb.append(str);
            }
            br.close();
        }catch (IOException e){
            e.printStackTrace();
        }
        finally{
            if (null != br){
                try{
                    br.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}
  1. 为了打印 response 中返回的数据,我们使用了 ResponseWrapper 来包装 Response,用于打印结果【博主此处声明,关于ResponseWrapper 是多年前百度别人博客并用于工作中的,具体出处已经忘了,请多多见谅!另外博主未懂ResponseWrapper 是如何起作用,为什么起到作用,现在把代码贴出来,记录一下,慢慢研究】
  2. ResponseWrapper.java
public class ResponseWrapper extends HttpServletResponseWrapper {

    private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    private PrintWriter printWriter = new PrintWriter(outputStream);

    public ResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    @Override
    public PrintWriter getWriter() throws IOException {
        return printWriter;
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException {
        return new ServletOutputStream() {
            @Override
            public boolean isReady() {
                return false;
            }
            @Override
            public void setWriteListener(WriteListener listener) {
            }
            @Override
            public void write(int b) throws IOException {
                outputStream.write(b);
            }
        };
    }
    public void flush(){
        try {
            printWriter.flush();
            printWriter.close();
            outputStream.flush();
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public ByteArrayOutputStream getByteArrayOutputStream(){
        return outputStream;
    }

    public String getTextContent() {
        flush();
        return outputStream.toString();
    }
}
  1. 具体ResponseWrapper代码介绍暂时无能为力,博主对于 Http请求、和Servlert容器理解还是不够透彻,以后再补全…见谅!
Logack:

最近在学习 LogBack 时发现一个非常有意思的功能,可以让 LogBack 帮我们打印 http 请求信息,配置如下,有兴趣的同学可以玩一玩

Maven 配置:


	  <!-- Logback -->
      <!--这个依赖直接包含了 logback-core 以及 slf4j-api的依赖-->
      <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.2.3</version>
      </dependency>

XML 新增打印 Http 请求配置:

     <!-- 访问日志,仅适用于Web后台 -->
    <logger name="AccessLog" level="INFO" additivity="false">
        <appender-ref ref="AccessFile"/>
        <!-- 生产环境删除stdout输出 -->
        <appender-ref ref="stdout"/>
    </logger>
    
	<!-- http 请求打印 -->
    <appender name="AccessFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${PROJECT_LOG_PATH}/access_logs/access.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${PROJECT_LOG_PATH}/access_logs/access-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>${DEFAULT_MAX_HISTORY}</maxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%msg%n</pattern>
        </encoder>
    </appender>
    
	<!-- 控制台日志打印 -->
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{40}:%L - %msg%n</pattern>
        </encoder>
    </appender>
大家有更好的想法,和意见可以评论多多交流,博主会第一时间更正!
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值