本来想写个 springboot-starter 来做 Request&Response 日志打印的,但是发现逛了一圈谷歌,发现基本都是用拦截器做的。
使用拦截器做本身没什么问题,但是 HttpServletRequest 读取过一次后,body 就不能再读取了,解决办法就是在 Filter 中自己提前包装一个可重复读的 Request,但是觉得这样做有点麻烦而且不那么优雅。
请教一下有什么优雅的办法在 SpringBoot 中打印 Request&Response 日志吗?
1
MoHen9 2020-09-22 11:58:10 +08:00 via Android
|
2
Kyle18Tang 2020-09-22 12:00:50 +08:00
|
3
springmarker OP @MoHen9 #1 AOP 早就试过了,但是 ctrl 层传入传出都是对象,打印日志还得序列化一下,感觉有点浪费
@Kyle18Tang #2 这个在 stackoverflow 见过,还没试过,想自己写个轻量级的 |
4
frankly123 2020-09-22 12:42:56 +08:00
/**
* @author wangyiwen * @version 1.0 Created in 2020/7/14 18:36 * 该注入是为了可以获取到包装过的 httpRequest */ public class WrapperRequestFilter extends AbstractRequestLoggingFilter { /** * Concrete subclasses should implement this method to write a log message * <i>before</i> the request is processed. * * @param request current HTTP request * @param message the message to log */ @Override protected void beforeRequest(HttpServletRequest request, String message) { //do nothing } /** * Concrete subclasses should implement this method to write a log message * <i>after</i> the request is processed. * * @param request current HTTP request * @param message the message to log */ @Override protected void afterRequest(HttpServletRequest request, String message) { //do nothing } } --------------------------------------------------------------------------------------------------- @Bean public WrapperRequestFilter wrapperRequestFilter() { WrapperRequestFilter wrapperRequestFilter = new WrapperRequestFilter(); wrapperRequestFilter.setIncludeQueryString(true); wrapperRequestFilter.setIncludeClientInfo(true); wrapperRequestFilter.setIncludeHeaders(true); wrapperRequestFilter.setIncludePayload(true); wrapperRequestFilter.setMaxPayloadLength(99999); return wrapperRequestFilter; } |
5
frankly123 2020-09-22 12:58:08 +08:00
@frankly123 然后想在哪里读就在哪里读
|
6
springmarker OP @frankly123 #4 request 的 inputstream 只能读取一次
|
7
hugedata 2020-09-22 14:16:21 +08:00
要么就在每个方法里手动打,要么就过滤器。
手动打有个好处:提高你的代码量从而提高绩效考核的分数。/doge |
8
letitbesqzr 2020-09-22 15:42:37 +08:00
@springmarker #6 使用 org.springframework.web.util.ContentCachingRequestWrapper
|
9
springmarker OP @letitbesqzr #8 这个用过,他的 InputStream 也是只能读取一次,应该是 contentBytes 可以读取多次,但是多次读取 contentBytes 的前提又是需要先做读取 InputStream 。
|
10
urzz 2020-09-22 16:36:59 +08:00
Spring MVC 的话,感觉可以考虑使用 RequestBodyAdvice 和 ResponseBodyAdvice 来实现,之前做参数加解密的时候用过这个。
打印日志不想序列化可以在 RequestBodyAdvice::beforeBodyRead 中做处理,处理完了之后返回一个新的 inputMessage 出来继续后面的 convert 应该就可以。 没试过,感觉可以尝试一下。 |
11
springmarker OP @urzz #10 这个我也试过,好像不支持 Form 表单
|
12
frankly123 2020-09-22 20:56:35 +08:00
@springmarker 看我代码,spring 帮你 wrapper 过了
|
13
CoderGeek 2020-09-22 21:16:38 +08:00
简洁 抽象类程序入口 befor 打印 request -> 你的各种逻辑 -> after 打印 response 打印 对象都写 toString 少用 json
|
14
xuanbg 2020-09-22 22:50:41 +08:00
最好是网关上写 Filter 打印,其次是各服务 AOP 打印。可以看: https://github.com/xuanbg/gateway
|
15
changdy 2020-09-23 08:29:44 +08:00
打印请求日志 可以使用 CommonsRequestLoggingFilter 或者调整 org.apache.coyote.http11.Http11InputBuffer 级别 . 不过最合适的
打印 Response 我也没找到很好的办法 网上找到一些都是获取的 pojo 类 . 插眼同蹲. ps 这种日志是不是在上层记录更合适? |
16
cs419 2020-09-23 08:55:13 +08:00 1
想要对 req 打印日志 打印的信息自然是要包含所有请求数据
因此需要读取 inputstream 那问题变成了 inputstream 能重复读取吗 看下接口设计 默认是不支持 但实现类可以调为支持重复读 一般 servlet inputstream 不支持重复读 想要支持就需要自己备份 觉着自己去备份不优雅 无非是觉着亲自给服务添加了个消耗性能的东西是脏活 最好 tomcat 中有个开关,配置下,就支持重复读了,这心里就好受了 脏活总得有人干 说白了君子远庖厨 眼不见为净 |
17
tanrenye 2020-09-23 09:43:10 +08:00
我的经验是用 filter,用 AOP 会有很多特定情境无法进入,日志丢失,例如参数不匹配之类的
|
18
springmarker OP @frankly123 #12 这个看了一下还是由抽象类封装为 ContentCachingRequestWrapper 的,这个类读取 InputStream 也是只能读取一次,读取 ContentBytes 有数据的前提也得是先读取 InputStream 。
@changdy #15 打日志就是为了方便一条龙查看,而且很多服务都是内部服务,没有网关。 @tanrenye #17 用 filter 的问题就是 Body 只能读取一次 |
19
aguesuka 2020-09-23 11:54:48 +08:00 via Android
不要在 spring 这一层做,要在网关做,今天记 requestBody 明天就想记 url header,后天就想记 tcp packet 。
|
20
springmarker OP @aguesuka #19 内部服务之间都是直连的,基本木得 gateway
|
21
tiankongzhe 2020-09-23 14:22:48 +08:00
哥,springboot 都有这个功能的提供的,只需要配置的开关打开就好了
spring.http.log-request-details=true |
22
springmarker OP @tiankongzhe #21 这个已经过时了,而且在 2.3.4 上没有生效
|
23
tiankongzhe 2020-09-23 14:51:00 +08:00
spring boot 2.1
logging.level.org.springframework.web=DEBUG spring.http.log-request-details=true spring boot 2.2 logging.level.org.springframework.web=DEBUG spring.mvc.log-request-details=true 配置上就好了,请求日志就有了 |
24
tiankongzhe 2020-09-23 14:52:22 +08:00
2.3 的官网找下吧,应该有这个配置的,之前的版本我是在官网 doc 上看到的
|
25
springmarker OP |
26
tiankongzhe 2020-09-23 15:04:29 +08:00
@springmarker 尝试用 javassist 吧,这到请求的总入口,加入 agent,这样比 filter 和 aop 都要简单,性能也高
|
27
fanyiaa 2020-09-27 12:00:27 +08:00
参考这个,把其中加密解密部分去掉就行了
https://gitee.com/ishuibo/rsa-encrypt-body-spring-boot |