springboot filter and interceptor实战之mdc日志打印

摘要:
1.1 MDC日志打印全局控制1.1.1日志配置<propertyname=“log.pattern”value=“%d{yyyy-MM-dd’HH:MM:ss.SSSXXX}%level[%thread][%logger{50}:%line][uuid:%X{operation_id}]%msg%n”></属性>1.1.2过滤器

1.1  mdc日志打印全局控制

1.1.1    logback配置

<property name="log.pattern" value="%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX}%level [%thread] [%logger{50}:%line] [uuid:%X{operation_id}] %msg%n"></property>

1.1.2    filter配置

 1 @WebFilter(filterName="logFilter", urlPatterns="/*")
 2 
 3 public class LogFilter implements Filter {
 4 
 5     private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class);
 6 
 7    
 8 
 9     @Override
10 
11     public void init(FilterConfig filterConfig) throws ServletException {
12 
13         log.info("---------log filter init");
14 
15     }
16 
17  
18 
19     @Override
20 
21     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
22 
23             throws IOException, ServletException {
24 
25         log.info("log filter doFilter");
26 
27         ServletRequest requestWrapper = null;
28 
29         requestWrapper = new MyRequestWrapper((HttpServletRequest) request);
30 
31         chain.doFilter(requestWrapper, response);
32 
33     }
34 
35  
36 
37     @Override
38 
39     public void destroy() {
40 
41         log.info("-------------log filter destroy");
42 
43        
44 
45     }
46 
47 }

同时需要在启动类加上扫描配置

 1 @SpringBootApplication()
 2 
 3 @ServletComponentScan       //扫描过滤器
 4 
 5 public class LogApplication {
 6 
 7  
 8 
 9    public static void main(String[] args) {
10 
11        Tools.setMdc(Tools.getUuid());
12 
13       SpringApplication.run(LogApplication.class, args);
14 
15    }
16 
17 }

1.1.3    自定义httpServletRequest

 1 public class MyRequestWrapper extends HttpServletRequestWrapper {
 2 
 3     private String body;
 4     private String requestMethod;
 5     
 6     public MyRequestWrapper(HttpServletRequest request) throws IOException {
 7         super(request);
 8         StringBuilder sb = new StringBuilder();
 9         String line = null;
10         BufferedReader reader = request.getReader();
11         requestMethod = request.getMethod();
12         
13         while((line = reader.readLine()) != null) {
14             sb.append(line);
15         }
16         body = sb.toString();
17     }
18     private boolean isPostOrPut() {
19         return "POST".equalsIgnoreCase(requestMethod) || "PUT".equalsIgnoreCase(requestMethod);
20     }
21     @Override
22     public String getQueryString() {
23         if(isPostOrPut()) {
24             return body;
25         } else {
26             return super.getQueryString();
27         }
28     }
29     @Override
30     public ServletInputStream getInputStream() throws IOException {
31         ByteArrayInputStream bais = new ByteArrayInputStream(body.getBytes(CommonVar.DEFAULT_CHARSET));
32         return new ServletInputStream() {
33             
34             @Override
35             public int read() throws IOException {
36                 return bais.read();
37             }
38 
39             @Override
40             public boolean isFinished() {
41                 return bais.available()==0;
42             }
43 
44             @Override
45             public boolean isReady() {
46                 return true;
47             }
48 
49             @Override
50             public void setReadListener(ReadListener listener) {
51                 
52             }
53         };
54     }
55 }

1.1.4    interceptor配置

 1 public class LogInterceptor implements HandlerInterceptor{
 2     private static final Logger log = LoggerFactory.getLogger(LogInterceptor.class);
 3     
 4     private static final String REQUEST_START_TIME = "request_start_time";
 5     
 6     @Override
 7     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
 8             throws Exception {
 9         setMdc(request);
10         logRequestMsg(request);
11         request.setAttribute(REQUEST_START_TIME, System.currentTimeMillis());
12         System.out.println("preHandle");
13         return true;
14     }
15 
16     @Override
17     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
18             ModelAndView modelAndView) throws Exception {
19         HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
20         System.out.println("postHandle");
21     }
22 
23     @Override
24     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
25             throws Exception {
26         logRequestEnd(request);
27         Tools.removeMdc();
28         System.out.println("afterCompletion");
29     }
30     private void setMdc(HttpServletRequest request) {
31         MDC.put("operation_id", Tools.getUuid());
32     }
33     /**
34      * log request url and request cost time
35      * @param request
36      */
37     private void logRequestEnd(HttpServletRequest request) {
38         long startTime = (long) request.getAttribute(REQUEST_START_TIME);
39         long endTime = System.currentTimeMillis();
40         long interval = endTime - startTime;
41         log.debug("request_end: {}, cost time:{}ms",request.getRequestURL(), interval);
42     }
43     /**
44      * log request url and param
45      * @param request 
46      * @throws IOException
47      */
48     private void logRequestMsg(HttpServletRequest request) throws IOException {
49         String url = request.getRequestURL().toString();
50         String method = request.getMethod();
51         String query = request.getQueryString();
52         log.debug("request_receive: url:{},method:{},query:{}", url, method, query);
53     }
54 }

新建config类将interceptor注册到spring

 1 @Configuration
 2 public class LogWebAppConfig implements WebMvcConfigurer  {
 3 
 4     @Override
 5     public void addInterceptors(InterceptorRegistry registry) {
 6         registry.addInterceptor(new LogInterceptor()).addPathPatterns("/**");
 7         WebMvcConfigurer.super.addInterceptors(registry);
 8     }
 9     
10 }

1.1.5    总体说明

1.1.5.1   功能说明

打印请求url,请求类型,请求参数

打印所有的请求耗时统计

使用logback的MDC机制打印日志,不了解的自行百度

1.1.5.2   问题(功能1)

post,put请求的参数在流里面,只能读取一次,如果在拦截器中读取了流,流就空了,controller层读取会报错。报错信息:

getReader() has already been called for this request preHandle

1.1.5.3 解决思想(功能1)

自定义一个httpServletRequest,提供post参数读取的方法(读完流之后将数据重新写回流中),然后重写getQueryString()方法。这个方法是get等请求获取参数的方式,在这里同样提供post请求的参数,减轻调用者的负担

使用过滤器过滤所有的请求,替换httpServletRequest为自定义的request,传递给下一条链

由于过滤器已经替换httpServletRequest,那么这里拿到的就是自定义的httpServletRequest,所以可以不用区分请求类型,直接调用getQueryString() 获取请求参数

1.1.5.4             其它功能实现

打印所有的请求耗时统计

  实现方式是在拦截器中为request增加一个字段(本文是request_start_time),记录当前时间,在方法调用完成后从request中拿到这个字段(起始时间),根据当前时间算出调用时间间隔。

使用logback打印MDC日志

      需要在logback中配置这么一个字段(本文是operation_id),然后在拦截器的入口为这个字段设置一个uuid即可。作用是每个请求对应的日志都有这个uuid。mdc实现原理是threadLocal,所以对于线程池需要额外处理方式。

1.1.6    结果

2018-12-08T11:46:23.916+08:00 INFO [localhost-startStop-1] [com.hikvision.log.common.filter.LogInterceptor:23] [uuid:] ---------log filter init

2018-12-08T11:46:33.307+08:00 INFO [http-nio-8080-exec-3] [com.hikvision.log.common.filter.LogInterceptor:29] [uuid:] log filter doFilter

2018-12-08T11:46:33.307+08:00 INFO [http-nio-8080-exec-2] [com.hikvision.log.common.filter.LogInterceptor:29] [uuid:] log filter doFilter

2018-12-08T11:46:33.307+08:00 INFO [http-nio-8080-exec-1] [com.hikvision.log.common.filter.LogInterceptor:29] [uuid:] log filter doFilter

2018-12-08T11:46:33.326+08:00 DEBUG [http-nio-8080-exec-1] [com.hikvision.log.common.filter.LogInterceptor:67] [uuid:0002] request_receive: url:http://127.0.0.1:8080/settings,method:GET,query:null

preHandle

2018-12-08T11:46:33.326+08:00 DEBUG [http-nio-8080-exec-2] [com.hikvision.log.common.filter.LogInterceptor:67] [uuid:0003] request_receive: url:http://127.0.0.1:8080/meta,method:GET,query:null

preHandle

2018-12-08T11:46:33.328+08:00 DEBUG [http-nio-8080-exec-3] [com.hikvision.log.common.filter.LogInterceptor:67] [uuid:0004] request_receive: url:http://127.0.0.1:8080/session,method:GET,query:null

preHandle

2018-12-08T11:46:33.368+08:00 INFO [http-nio-8080-exec-2] [com.hikvision.log.web.restful.CheckDiskController:45] [uuid:0003] Get availableSpace is:173138524

2018-12-08T11:46:33.369+08:00 INFO [http-nio-8080-exec-2] [com.hikvision.log.web.restful.CheckDiskController:49] [uuid:0003] Get omcVersion is:

postHandle

2018-12-08T11:46:33.453+08:00 DEBUG [http-nio-8080-exec-3] [com.hikvision.log.common.filter.LogInterceptor:56] [uuid:0004] request_end: http://127.0.0.1:8080/session, cost time:124ms

afterCompletion

postHandle

2018-12-08T11:46:33.455+08:00 DEBUG [http-nio-8080-exec-2] [com.hikvision.log.common.filter.LogInterceptor:56] [uuid:0003] request_end: http://127.0.0.1:8080/meta, cost time:129ms

afterCompletion

postHandle

2018-12-08T11:46:33.473+08:00 DEBUG [http-nio-8080-exec-1] [com.hikvision.log.common.filter.LogInterceptor:56] [uuid:0002] request_end: http://127.0.0.1:8080/settings, cost time:147ms

afterCompletion

免责声明:文章转载自《springboot filter and interceptor实战之mdc日志打印》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇深入浅出Blazor webassembly 之API服务端保护[System] CentOS虚拟机系统克隆后的网络配置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

实用向—总结一些唯一ID生成方式

在日常的项目开发中,我们经常会遇到需要生成唯一ID的业务场景,不同的业务对唯一ID的生成方式与要求都会不尽相同,一是生成方式多种多样,如UUID、雪花算法、数据库递增等;其次业务要求上也各有不同,有的只要保证唯一性即可,有的需要加上时间戳,有的要保证按顺序递增等。以下是我结合实际业务中的使用总结了几种唯一ID的生成方式,  要求就是在一般的应用场景下一方面...

使用自增主键、UUID的优缺点

自增主键 这种方式是使用数据库提供的自增数值型字段作为自增主键,它的优点是: 数据库自动编号,速度快,而且是增量增长,按顺序存放,对于检索非常有利; 数字型,占用空间小,易排序,在程序中传递也方便; 能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。 保证生成的ID不仅是表独立的,而且是库独立的,这点在你想切分数据库的时候尤为重要。 缺点 :...

Netty (1)

目录 Netty(1) 传统的阻塞I/O模型(BIO) 特点 问题 事件驱动模型 Netty高效的 Reactor 线程模型 Reactor单线程模型 Reactor多线程模型 Reactor主从多线程模型 Netty特性 多路复用模型 数据零拷贝 无锁化设计 高性能的序列化框架 Netty(1) 官网的介绍,Netty 是...

深入理解NIO(二)—— Tomcat中对NIO的应用

深入理解NIO(二)—— Tomcat中对NIO的应用 老哥行行好,转载和我说一声好吗,我不介意转载的,但是请把原文链接贴大点好吗 Tomcat大致架构 先贴两张图大致看一眼Tomcat的架构 Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container; Ser...

物联网安全拔“牙”实战——低功耗蓝牙(BLE)初探

0x00 目录 0x00 目录 0x01 前言 0x02 BLE概述 BLE 协议栈总览 GAP-通用访问规范 GATT-通用属性协议 0x03 BLE嗅探 0x04 伪造BLE通信 0x05 分析BLE私有数据协议(灯泡、跳蛋、小米手环) 1.YeeLight 2 代蓝牙灯泡 2.小爱爱智能跳蛋(这个真不是我的,某个小伙伴借给我研究的) 3.小...

linux下安装使用libuuid(uuid-generate)

linux下安装使用libuuid(uuid-generate) linux下安装使用libuuid(uuid-generate) UUID简介 安装libuuid库 编写一个程序试一下 代码 编译运行 UUID简介 UUID含义是通用唯一识别码(Universally Unique Identifier),这 是一个软件建构的标准,也是被开源...