这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战
本系列代码地址:github.com/JoJoTec/spr…
网关由于是所有外部用户请求的入口,记录这些请求中我们需要的元素,对于线上监控以及业务问题定位,是非常重要的。并且,在这些元素中,链路信息也是非常重要的。通过链路信息,我们可以找到请求调用全链路相关的日志。并且,网关也是大部分请求链路起始的地方,记录请求中的元素的同时,也要带上链路信息。
我们需要在网关记录每个请求的:
- HTTP 相关元素:
- URL 相关信息
- 请求信息,例如 HTTP HEADER,请求时间等等
- 某些类型的请求体
- 响应信息,例如响应码
- 某些类型响应的响应体
- 链路信息
现有的可供分析的日志以及缺陷
首先我们来看 Spring Cloud Gateway 中本身我们可以利用的日志。Spring Cloud Gateway 基于 Spring-WebFlux,Spring-WebFlux 基于 Project Reactor,我们没有在网关加入额外的 Web 容器依赖,所以 Web 容器用的是默认的基于 Project Reactor 的 reactor-netty 实现的 Web 容器。
netty 抓包日志
使用了 netty 我们可以联想到 netty 的抓包日志,Spring Cloud Gateway 封装了这个功能,并暴露了配置。Spring Cloud Gateway 是一个网关,他会作为 HTTP 服务器接受 HTTP 请求的同时,还在作为 HTTP 客户端将请求转发到下游的微服务。所以,这里有两种抓包日志,一种是作为 HTTP 服务器接收到的请求和响应,另一种是做为 HTTP 客户端发出的请求和响应。分别对应两个配置:
1 | yaml复制代码spring.cloud.gateway: |
经常有读者私信我问如何看 spring cloud 有哪些配置,官方文档感觉不够清楚全面。我一般是看源码,但是鉴于很多人没有精力去研究源码,一种偷懒的方式是去看 jar 包里面的 spring-configuration-metadata.json
。里面包含了比较全的配置,以及配置类(如果存在的话),这是很方便的。例如我们这里的两个配置,在这个 json 中对应:
1 | json复制代码{ |
可以看出,spring.cloud.gateway.httpclient.wiretap
对应配置类 org.springframework.cloud.gateway.config.HttpClientProperties
(这个配置类里面的配置我们后面还会用到,到时候会详细分析其中的配置项),默认为 false。spring.cloud.gateway.httpserver.wiretap
没有配置类,他是被直接使用的,对应源码:
1 | less复制代码@Bean |
增加这两个配置为 true 之后,增加日志输出配置,我这里使用的是 log4j2(其实就是设置 reactor.netty.http.server.HttpServer
和 reactor.netty.http.server.HttpClient
的日志级别为 DEBUG):
1 | ini复制代码<AsyncLogger name="reactor.netty.http.server.HttpServer" level="debug" additivity="false" includeLocation="true"> |
然后,再次请求我们前面实现的网关,可以看到类似于下面的日志:
1 | ini复制代码2021-11-27 01:16:06,262 DEBUG [sports,,] [10632] [reactor-http-nio-2][io.netty.util.internal.logging.AbstractInternalLogger:145]:[id:1277d54e, L:/127.0.0.1:8181 - R:/127.0.0.1:52797] READ: 466B |
可以从日志中看出下面比较重要的信息:
- 从抓包日志从可以得出 HTTP 请求与响应包中的所有内容。
- 这里的日志,由于没被 spring-cloud-sleuth 包装,所以日志本身的占位符没有相关链路信息
- 但是从包中,可以看到 spring-cloud-sleuth 的链路信息,这里是:
"X-B3-Parentspanid": "ef9653a0003ed68e", "X-B3-Sampled": "0", "X-B3-Spanid": "82b6a0202dc1566b", "X-B3-Traceid": "ef9653a0003ed68e",
- netty 本身有 traceId,即这里的
6666c1d1
和1277d54e
(分别是作为 HttpServer 接受请求和作为 HttpClient 转发请求后台微服务),这样其实就可以将 spring-cloud-sleuth 的链路信息与 netty 的链路信息结合在一起
这些日志很全面,但是有下面一些不好用的问题:
- 只能输出所有请求响应的包内容,并不能定制输出哪些内容。例如对于文件上传请求,我们其实并不想看他的请求体,但是这里无法定制化。
- 日志太多了,会影响我们的性能。
HttpWebHandlerAdapter 的 TRACE 日志
在系列前面文章的源码分析中,我们知道在入口的 HttpWebHandlerAdapter 中有请求与响应日志,可以通过下面的配置进行启用:
1 | ini复制代码<AsyncLogger name="org.springframework.web.server.adapter.HttpWebHandlerAdapter" level="trace" additivity="false" includeLocation="true"> |
这样就能启用类似于下面的日志:
1 | ini复制代码2021-11-27 01:25:28,472 TRACE [sports,,] [16188] [reactor-http-nio-2][org.springframework.core.log.LogFormatUtils:88]:[8d5138d1-3] HTTP GET "/test-ss/anything", headers={masked} |
如何让 headers 不要被掩码,可以通过下面的配置实现:
1 | yaml复制代码spring.codec: |
这个配置对应的配置类是:CodecProperties
,通过源码我们也可以发现,我们还可以在这里限制 body 的大小:
1 | kotlin复制代码@ConfigurationProperties(prefix = "spring.codec") |
加上配置后,日志如下:
1 | ini复制代码2021-11-27 01:32:50,501 TRACE [sports,,] [17668] [reactor-http-nio-2][org.springframework.core.log.LogFormatUtils:88]:[ecf9f55a-1] HTTP GET "/test-ss/anything", headers=[Host:"127.0.0.1:8181", Connection:"keep-alive", Cache-Control:"max-age=0", Upgrade-Insecure-Requests:"1", User-Agent:"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3879.400 QQBrowser/10.8.4552.400", Accept:"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", Accept-Encoding:"gzip, deflate, br", Accept-Language:"zh-CN,zh;q=0.9"] |
但是这个日志内容太少,只包含请求和响应头,并且没有 spring-cloud-sleuth 相关的链路信息。
AccessLog
Spring Cloud Gateway 使用基于 reactor-netty 的 http 服务器,在 spring-boot 的封装中,可以通过添加 NettyServerCustomizer
Bean 来实现添加自定义 AccessLog。AccessLog 包含的元素有:
1 | less复制代码public interface AccessLogArgProvider { |
可以看出里面没有 spring cloud sleuth 相关的链路信息,但是我们可以通过实现 Global Filter 将链路信息加入 Response 的 Header 中,并且通过 AccessLogArgProvider
的 requestHeader
将这些 Header 输出到 accesslog:
1 | ini复制代码package com.github.jojotech.spring.cloud.apigateway.filter; |
然后,通过实现 NettyServerCustomizer
注册一个 Bean 来配置 Access Log。
1 | java复制代码package com.github.jojotech.spring.cloud.apigateway.filter; |
这样,我们就可以得到像下面的 access log:
1 | ini复制代码2021-11-27 03:56:10,948 INFO [sports,,] [8536] [reactor-http-nio-2][reactor.util.Loggers$Slf4JLogger:269]:baa69b779a8497eb,baa69b779a8497eb -> 127.0.0.1:64196 - - [2021-11-27T03:56:10.720844200Z[Etc/GMT]] "GET /test-ss/anything HTTP/1.1" 200 1084 228 ms |
accesslog 的也不能输出 body,所以我们还是需要定制自己的日志 Filter。
我们在下一节,会开始实现我们自己定制的日志 Filter
微信搜索“我的编程喵”关注公众号,每日一刷,轻松提升技术,斩获各种offer:
本文转载自: 掘金