定制开发Springcloud Gateway网关日志这样打才完美

定制开发日志打印收集是开发中定制开发调试和定位线上问题的定制开发关键手段也是重中之重,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<Void> 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<Object> reqArgs = new ArrayList<>();      reqLog.append("\================ Gateway Request Start  ================\");      // 打印路由添加占位符      reqLog.append("===> {}: {}\");      // 参数      String requestMethod = request.getMethodValue();      reqArgs.add(requestMethod);      reqArgs.add(requestUrl);      // 打印请求头      HttpHeaders headers = request.getHeaders();      headers.forEach((headerName, headerValue) -> {         reqLog.append("===Headers===  {}: {}\");         reqArgs.add(headerName);         //如果有token,可以先把token解析后打印出原始报文,JwtUtil替换成自己项目里工具类         if (AUTH_KEY.toLowerCase().equals(headerName)) {            String value = headerValue.get(0);            String token = JwtUtil.getToken(value);            Claims claims = JwtUtil.parseJWT(token);            reqArgs.add((claims == null) ? "" : claims.toString());            reqLog.append("===Headers===  {}: {}\");            reqArgs.add(headerName.concat("-original"));            reqArgs.add(StringUtils.join(headerValue.toArray()));         } else {            reqArgs.add(StringUtils.join(headerValue.toArray()));         }      });      reqLog.append("================  Gateway Request End  =================\");      // 打印执行时间      log.info(reqLog.toString(), beforeReqArgs.toArray());      return chain.filter(exchange);   }       private String getOriginalRequestUrl(ServerWebExchange exchange) {            ServerHttpRequest request = exchange.getRequest();            LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);            URI requestUri = uris.stream().findFirst().orElse(request.getURI());            MultiValueMap<String, String> queryParams = request.getQueryParams();            return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();    }    @Override   public int getOrder() {      return Ordered.LOWEST_PRECEDENCE;   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

ResponseLogFilter

请求的响应报文不在这里打印,post body 参数没有打印

@Slf4j@Configuration@ConditionalOnProperty(value = "log.request.enabled", havingValue = "true", matchIfMissing = true)public class GlobalResponseLogFilter implements GlobalFilter, Ordered {   @Override   public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {      ServerHttpRequest request = exchange.getRequest();      // 打印请求路径      String path = request.getPath().pathWithinApplication().value();      return chain.filter(exchange).then(         Mono.fromRunnable(() -> {            MultiValueMap<String, String> queryParams = request.getQueryParams();            String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();            // 构建成一条长日志            StringBuilder responseLog = new StringBuilder(200);            // 日志参数            List<Object> responseArgs = new ArrayList<>();            responseLog.append("\================ Gateway Response Start  ================\");            ServerHttpResponse response = exchange.getResponse();            // 状态码个path占位符: 200 get: /xxx/xxx/xxx?a=b            responseLog.append("<=== {} {}: {}\");            // 参数            String requestMethod = request.getMethodValue();            responseArgs.add(response.getStatusCode().value());            responseArgs.add(requestMethod);            responseArgs.add(requestUrl);            // 打印请求头            HttpHeaders headers = response.getHeaders();            headers.forEach((headerName, headerValue) -> {               responseLog.append("===Headers===  {}: {}\");               responseArgs.add(headerName);               responseArgs.add(StringUtils.join(headerValue.toArray()));            });            responseLog.append("================  Gateway Response End  =================\");            // 打印执行时间            log.info(responseLog.toString(), responseArgs.toArray());         })      );   }   @Override   public int getOrder() {      return Ordered.HIGHEST_PRECEDENCE;   }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

打印完整格式

================ 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  =================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
================ 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  =================
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发