日志打印收集是开发中调试和定位线上问题的关键手段也是重中之重,gateway作为请求入口转发的核心模块,合理、规范的日志打印很重要。
- 这里只进行日志的打印输出,不做收集,收集汇总工作可以结合ELK,监控日志文件进行同步。
- 实现方式使用 gateway 的 GlobalFilter 过滤器。
- 请求日志打印的过滤器排序尽量低一些。
- 打印日志时,注意避免多次打印造成并发请求日志错乱,可以拼接一个大的日志串,一次打印输出。
RequestLogFilter
增加一个请求入参过滤器,用来打印入参信息。
@Slf4j
@Configuration
@ConditionalOnProperty(value = "log.request.enabled", havingValue = "true", matchIfMissing = true)
public class RequestLogFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 打印请求路径
String path = request.getPath().pathWithinApplication().value();
// 打印请求url
String requestUrl = this.getOriginalRequestUrl(exchange);
// **构建成一条长 日志,避免并发下日志错乱**
StringBuilder reqLog = new StringBuilder(200);
// 日志参数
List
ResponseLogFilter
网关请求的响应报文不在这里打印,post body 参数没有打印
@Slf4j
@Configuration
@ConditionalOnProperty(value = "log.request.enabled", havingValue = "true", matchIfMissing = true)
public class GlobalResponseLogFilter implements GlobalFilter, Ordered {
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 打印请求路径
String path = request.getPath().pathWithinApplication().value();
return chain.filter(exchange).then(
Mono.fromRunnable(() -> {
MultiValueMap queryParams = request.getQueryParams();
String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();
// 构建成一条长日志
StringBuilder responseLog = new StringBuilder(200);
// 日志参数
List
打印完整格式
================ Gateway Request Start ================
===> GET: /xx/xx/xx/get?scope=1&type=1
===Headers=== Host: xx.xx.xx.com:10001
===Headers=== X-Request-ID: 84cd4a9a6cb67a7c21e1211d7ac8a236
===Headers=== X-Real-IP: 218.29.167.226
===Headers=== X-Forwarded-For: 218.29.167.226
===Headers=== X-Forwarded-Host: xx.xx.xx.com:10001
===Headers=== X-Forwarded-Port: 443
===Headers=== X-Forwarded-Proto: https
===Headers=== X-Forwarded-Scheme: https
===Headers=== X-Scheme: https
===Headers=== sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"
===Headers=== auth-token: {tenant_id=100000, xxx:xxx}
===Headers=== auth-token-original: bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRfaWQiOiIwxxx
===Headers=== sec-ch-ua-mobile: ?0
===Headers=== user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36
===Headers=== content-type: application/json;charset=utf-8
===Headers=== accept: application/json, text/plain, */*
===Headers=== tenant-id: 100000
===Headers=== sec-ch-ua-platform: "Windows"
===Headers=== origin: https://xx.xx.xx.com
===Headers=== sec-fetch-site: same-site
===Headers=== sec-fetch-mode: cors
===Headers=== sec-fetch-dest: empty
===Headers=== referer: https://xx.xx.xx.com/
===Headers=== accept-encoding: gzip, deflate, br
===Headers=== accept-language: zh-CN,zh-TW;q=0.9,zh;q=0.8,en-US;q=0.7,en;q=0.6
================ Gateway Request End =================
================ Gateway Response Start ================
<=== 200 GET: /xx/xx/xx/get?scope=1&type=1
===Headers=== transfer-encoding: chunked
===Headers=== Access-Control-Allow-Headers: X-Requested-With, Tenant-Id, Auth-Token, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client
===Headers=== Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS,HEAD
===Headers=== Access-Control-Allow-Origin: *
===Headers=== Access-Control-Expose-Headers: *
===Headers=== Access-Control-Max-Age: 18000L
===Headers=== Access-Control-Allow-Credentials: true
===Headers=== Content-Type: application/json;charset=UTF-8
===Headers=== Date: Thu, 16 Jun 2022 10:15:24 GMT
================ Gateway Response End =================