如何配置Spring Cloud Gateway以记录请求应答的详细数据日志?

2026-04-30 08:162阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计865个文字,预计阅读时间需要4分钟。

如何配置Spring Cloud Gateway以记录请求应答的详细数据日志?

我尽量简洁地改写了您提供的代码:

javapublic class GatewayContext { public static final String CACHE_GATEWAY_CONTEXT=cacheGatewayContext; // 缓存JSON体 private String cacheBody; // 缓存formData private String cacheFormData;}

我就废话不多说了,大家还是直接看代码吧~

public class GatewayContext { public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext"; /** * cache json body */ private String cacheBody; /** * cache formdata */ private MultiValueMap<String, String> formData; /** * cache reqeust path */ private String path; public String getCacheBody() { return cacheBody; } public void setCacheBody(String cacheBody) { this.cacheBody = cacheBody; } public MultiValueMap<String, String> getFormData() { return formData; } public void setFormData(MultiValueMap<String, String> formData) { this.formData = formData; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } }

import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.segmentfault.com/a/1190000017898354 @Component public class LogRequestGlobalFilter implements GlobalFilter { /** * default HttpMessageReader */ private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders(); private Logger log = LoggerFactory.getLogger(LogRequestGlobalFilter.class); @Override public Mono<Void> filter( ServerWebExchange exchange, GatewayFilterChain chain) { /** * save request path and serviceId into gateway context */ ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().pathWithinApplication().value(); GatewayContext gatewayContext = new GatewayContext(); gatewayContext.setPath(path); /** * save gateway context into exchange */ exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext); HttpHeaders headers = request.getHeaders(); MediaType contentType = headers.getContentType(); log.info("start-------------------------------------------------"); log.info("HttpMethod:{},Url:{}", request.getMethod(), request.getURI().getRawPath()); log.info("Headers token: {}", headers.getFirst("token")); if (request.getMethod() == HttpMethod.GET) { log.info("end-------------------------------------------------"); } if (request.getMethod() == HttpMethod.POST) { Mono<Void> voidMono = null; if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) { voidMono = readBody(exchange, chain, gatewayContext); } if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { voidMono = readFormData(exchange, chain, gatewayContext); } return voidMono; } /* log.debug( "[GatewayContext]ContentType:{},Gateway context is set with {}", contentType, gatewayContext);*/ return chain.filter(exchange); } /** * ReadFormData * * @param exchange * @param chain * @return */ private Mono<Void> readFormData( ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) { final ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); return exchange.getFormData() .doOnNext(multiValueMap -> { gatewayContext.setFormData(multiValueMap); log.info("Post x-www-form-urlencoded:{}", multiValueMap); log.info( "end-------------------------------------------------"); }) .then(Mono.defer(() -> { Charset charset = headers.getContentType().getCharset(); charset = charset == null ? StandardCharsets.UTF_8 : charset; String charsetName = charset.name(); MultiValueMap<String, String> formData = gatewayContext.getFormData(); /** * formData is empty just return */ if (null == formData || formData.isEmpty()) { return chain.filter(exchange); } StringBuilder formDataBodyBuilder = new StringBuilder(); String entryKey; List<String> entryValue; try { /** * repackage form data */ for (Map.Entry<String, List<String>> entry : formData .entrySet()) { entryKey = entry.getKey(); entryValue = entry.getValue(); if (entryValue.size() > 1) { for (String value : entryValue) { formDataBodyBuilder.append(entryKey).append("=") .append( URLEncoder.encode(value, charsetName)) .append("&"); } } else { formDataBodyBuilder .append(entryKey).append("=").append(URLEncoder .encode(entryValue.get(0), charsetName)) .append("&"); } } } catch (UnsupportedEncodingException e) { // ignore URLEncode Exception } /** * substring with the last char '&' */ String formDataBodyString = ""; if (formDataBodyBuilder.length() > 0) { formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1); } /** * get data bytes */ byte[] bodyBytes = formDataBodyString.getBytes(charset); int contentLength = bodyBytes.length; ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( request) { /** * change content-length * * @return */ @Override public HttpHeaders getHeaders() { HttpHeaders github.com/spring-cloud/spring-cloud-gateway/pull/1095 */ byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); DataBufferUtils.retain(buffer); return Mono.just(buffer); }); /** * repackage ServerHttpRequest */ ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; /** * mutate exchage with new ServerHttpRequest */ ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build(); /** * read body string with default messageReaders */ return ServerRequest.create(mutatedExchange, messageReaders) .bodyToMono(String.class) .doOnNext(objectValue -> { log.info("PostBody:{}", objectValue); log.info( "end-------------------------------------------------"); gatewayContext.setCacheBody(objectValue); /* log.debug("[GatewayContext]Read JsonBody:{}", objectValue);*/ }).then(chain.filter(mutatedExchange)); }); } }

import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.github.com/qiaomengnan16/gateway-2x-log-demo

总结

gateway和zuul打印参数的方式思路是一致的,只是gateway采用的是reactor,写法上与zuul的直接读取流有些不同,这里需要知道的是Flux需要转换为Mono这个地方,如果不转换容易分多批打印。

以上这篇Spring Cloud Gateway 记录请求应答数据日志操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持易盾网络。

如何配置Spring Cloud Gateway以记录请求应答的详细数据日志?

本文共计865个文字,预计阅读时间需要4分钟。

如何配置Spring Cloud Gateway以记录请求应答的详细数据日志?

我尽量简洁地改写了您提供的代码:

javapublic class GatewayContext { public static final String CACHE_GATEWAY_CONTEXT=cacheGatewayContext; // 缓存JSON体 private String cacheBody; // 缓存formData private String cacheFormData;}

我就废话不多说了,大家还是直接看代码吧~

public class GatewayContext { public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext"; /** * cache json body */ private String cacheBody; /** * cache formdata */ private MultiValueMap<String, String> formData; /** * cache reqeust path */ private String path; public String getCacheBody() { return cacheBody; } public void setCacheBody(String cacheBody) { this.cacheBody = cacheBody; } public MultiValueMap<String, String> getFormData() { return formData; } public void setFormData(MultiValueMap<String, String> formData) { this.formData = formData; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } }

import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.segmentfault.com/a/1190000017898354 @Component public class LogRequestGlobalFilter implements GlobalFilter { /** * default HttpMessageReader */ private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders(); private Logger log = LoggerFactory.getLogger(LogRequestGlobalFilter.class); @Override public Mono<Void> filter( ServerWebExchange exchange, GatewayFilterChain chain) { /** * save request path and serviceId into gateway context */ ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().pathWithinApplication().value(); GatewayContext gatewayContext = new GatewayContext(); gatewayContext.setPath(path); /** * save gateway context into exchange */ exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT, gatewayContext); HttpHeaders headers = request.getHeaders(); MediaType contentType = headers.getContentType(); log.info("start-------------------------------------------------"); log.info("HttpMethod:{},Url:{}", request.getMethod(), request.getURI().getRawPath()); log.info("Headers token: {}", headers.getFirst("token")); if (request.getMethod() == HttpMethod.GET) { log.info("end-------------------------------------------------"); } if (request.getMethod() == HttpMethod.POST) { Mono<Void> voidMono = null; if (MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)) { voidMono = readBody(exchange, chain, gatewayContext); } if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { voidMono = readFormData(exchange, chain, gatewayContext); } return voidMono; } /* log.debug( "[GatewayContext]ContentType:{},Gateway context is set with {}", contentType, gatewayContext);*/ return chain.filter(exchange); } /** * ReadFormData * * @param exchange * @param chain * @return */ private Mono<Void> readFormData( ServerWebExchange exchange, GatewayFilterChain chain, GatewayContext gatewayContext) { final ServerHttpRequest request = exchange.getRequest(); HttpHeaders headers = request.getHeaders(); return exchange.getFormData() .doOnNext(multiValueMap -> { gatewayContext.setFormData(multiValueMap); log.info("Post x-www-form-urlencoded:{}", multiValueMap); log.info( "end-------------------------------------------------"); }) .then(Mono.defer(() -> { Charset charset = headers.getContentType().getCharset(); charset = charset == null ? StandardCharsets.UTF_8 : charset; String charsetName = charset.name(); MultiValueMap<String, String> formData = gatewayContext.getFormData(); /** * formData is empty just return */ if (null == formData || formData.isEmpty()) { return chain.filter(exchange); } StringBuilder formDataBodyBuilder = new StringBuilder(); String entryKey; List<String> entryValue; try { /** * repackage form data */ for (Map.Entry<String, List<String>> entry : formData .entrySet()) { entryKey = entry.getKey(); entryValue = entry.getValue(); if (entryValue.size() > 1) { for (String value : entryValue) { formDataBodyBuilder.append(entryKey).append("=") .append( URLEncoder.encode(value, charsetName)) .append("&"); } } else { formDataBodyBuilder .append(entryKey).append("=").append(URLEncoder .encode(entryValue.get(0), charsetName)) .append("&"); } } } catch (UnsupportedEncodingException e) { // ignore URLEncode Exception } /** * substring with the last char '&' */ String formDataBodyString = ""; if (formDataBodyBuilder.length() > 0) { formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1); } /** * get data bytes */ byte[] bodyBytes = formDataBodyString.getBytes(charset); int contentLength = bodyBytes.length; ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( request) { /** * change content-length * * @return */ @Override public HttpHeaders getHeaders() { HttpHeaders github.com/spring-cloud/spring-cloud-gateway/pull/1095 */ byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); DataBufferUtils.retain(buffer); return Mono.just(buffer); }); /** * repackage ServerHttpRequest */ ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; /** * mutate exchage with new ServerHttpRequest */ ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build(); /** * read body string with default messageReaders */ return ServerRequest.create(mutatedExchange, messageReaders) .bodyToMono(String.class) .doOnNext(objectValue -> { log.info("PostBody:{}", objectValue); log.info( "end-------------------------------------------------"); gatewayContext.setCacheBody(objectValue); /* log.debug("[GatewayContext]Read JsonBody:{}", objectValue);*/ }).then(chain.filter(mutatedExchange)); }); } }

import lombok.extern.slf4j.Slf4j; import org.reactivestreams.Publisher; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.github.com/qiaomengnan16/gateway-2x-log-demo

总结

gateway和zuul打印参数的方式思路是一致的,只是gateway采用的是reactor,写法上与zuul的直接读取流有些不同,这里需要知道的是Flux需要转换为Mono这个地方,如果不转换容易分多批打印。

以上这篇Spring Cloud Gateway 记录请求应答数据日志操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持易盾网络。

如何配置Spring Cloud Gateway以记录请求应答的详细数据日志?