This series code address: github.com/JoJoTec/spr…
In this section, we implement a GlobalFilter for public logging, based on the Publisher factory with link information implemented earlier. To review our requirements:
We need to log each request at the gateway:
- HTTP related elements:
- URL Related Information
- Request information, such as HTTP headers, request time, and so on
- Some type of request body
- Response information, such as response code
- The response body of some type of response
- The link information
Note what needs to be noted about the Body of the request and response
As mentioned in the previous section, if the body processing of the request and response is put into the master link, the link information of Spring Cloud Sleuth will be lost. There are two other things to note:
- TCP sticky packet unpacking causes a request body to be split into several pieces or a packet to contain several requests
- After reading, release the DataBuffer read from the original request body
Why release the DataBuffer read from the original request body? The underlying count does not return to zero, causing a memory leak if the DataBuffer is not released manually. The DataBuffer needs to be released manually, as shown in the framework code:
ByteBufferDecoder.java
@Override
public ByteBuffer decode(DataBuffer dataBuffer, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
int byteCount = dataBuffer.readableByteCount();
ByteBuffer copy = ByteBuffer.allocate(byteCount);
copy.put(dataBuffer.asByteBuffer());
copy.flip();
DataBufferUtils.release(dataBuffer);
if (logger.isDebugEnabled()) {
logger.debug(Hints.getLogPrefix(hints) + "Read " + byteCount + " bytes");
}
return copy;
}
Copy the code
We want to convert the body of DataBuffer into a string that can be printed to the log. For code brevity and error protection, we use a utility class to read the DataBuffer into a string and release it:
package com.github.jojotech.spring.cloud.apigateway.common; import com.google.common.base.Charsets; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferUtils; public class BufferUtil { public static String dataBufferToString(DataBuffer dataBuffer) { byte[] content = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(content); DataBufferUtils.release(dataBuffer); return new String(content, Charsets.UTF_8); }}Copy the code
Write GlobalFilter to implement the public logging
With that in mind, we can finally start writing the GlobalFilter log:
package com.github.jojotech.spring.cloud.apigateway.filter; import java.net.URI; import java.util.Set; import com.alibaba.fastjson.JSON; import com.github.jojotech.spring.cloud.apigateway.common.BufferUtil; import com.github.jojotech.spring.cloud.apigateway.common.TracedPublisherFactory; import lombok.extern.log4j.Log4j2; import org.reactivestreams.Publisher; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import org.springframework.beans.factory.annotation.Autowired; 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.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.http.server.reactive.ServerHttpResponseDecorator; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; @Log4j2 @Component public class CommonLogFilter implements GlobalFilter, Public static final Set<MediaType> legalLogMediaTypes = set.of (MediaType.TEXT_XML, Ordered {// Can output body format public static final Set<MediaType> legalLogMediaTypes = set.of (MediaType. MediaType.TEXT_PLAIN, MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON ); @Autowired private TracedPublisherFactory tracedPublisherFactory; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { long startTime = System.currentTimeMillis(); ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); DataBufferFactory DataBufferFactory = Response.bufferFactory (); // HttpHeaders requestHeaders = request.getheaders (); / / request body type MediaType requestContentType = requestHeaders. GetContentType (); // Request uri String uri = request.geturi ().tostring (); // Request HTTP method HttpMethod method = request.getMethod(); log.info("{} -> {}: header: {}", method, uri, JSON.toJSONString(requestHeaders)); Flux<DataBuffer> dataBufferFlux = tracedPublisherFactory.getTracedFlux(request.getBody(), Buffer ().map(dataBuffers -> {// Glue all buffers together DataBuffer DataBuffer = dataBufferFactory.join(dataBuffers); // Only when debug is enabled, Will only output the body if (the isDebugEnabled ()) {/ / output only a specific body type concrete the if (legalLogMediaTypes. The contains (requestContentType)) {a try {// Convert the body stream to a String for output. Also note that the original buffer needs to be released because the body stream has already been read. But there is no local recycling / / reference String s = BufferUtil. DataBufferToString (dataBuffer); log.debug("body: {}", s); dataBuffer = dataBufferFactory.wrap(s.getBytes()); } catch (Exception e) { log.error("error read request body: {}", e.getMessage(), e); } } else { log.debug("body: {}", request); } } return dataBuffer; }); return chain.filter(exchange.mutate().request(new ServerHttpRequestDecorator(request) { @Override public Flux<DataBuffer> getBody() { return dataBufferFlux; } }).response(new ServerHttpResponseDecorator(response) { @Override public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { HttpHeaders responseHeaders = super.getHeaders(); // Write the response back to the client's HttpClientConnect callback, which has jumped out of Spring Cloud Sleuth's link Span, Log.info (" Response: {} -> {} {} Header: {}, time: {}ms", method, uri, getStatusCode(), JSON.toJSONString(responseHeaders), System.currentTimeMillis() - startTime); final MediaType contentType = responseHeaders.getContentType(); if (contentType ! = null && body instanceof Flux && legalLogMediaTypes. The contains (contentType) && the isDebugEnabled ()) {/ / TCP sticky problem of unpacking, FluxBody <? FluxBody <? FluxBody <? FluxBody <? extends DataBuffer> fluxBody = tracedPublisherFactory.getTracedFlux(Flux.from(body), exchange); return super.writeWith(fluxBody.buffer().map(buffers -> { DataBuffer buffer = dataBufferFactory.join(buffers); try { String s = BufferUtil.dataBufferToString(buffer); log.debug("response: body: {}", s); return dataBufferFactory.wrap(s.getBytes()); } catch (Exception e) { log.error("error read response body: {}", e.getMessage(), e); } return buffer; })); } // if body is not a flux. never got there. return super.writeWith(body); } }).build()); } @override public int getOrder() {CommonTraceFilter (); Return new Tracefilter ().getOrder() + 1; }}Copy the code
Points needing attention are clearly marked in the notes, please refer to.
See the log
We turn on logging for the body by adding the following logging configuration, so that the logging is complete:
<AsyncLogger name="com.github.jojotech.spring.cloud.apigateway.filter.CommonLogFilter" level="debug" additivity="false" includeLocation="true">
<appender-ref ref="console" />
</AsyncLogger>
Copy the code
Send a request for POST with body, as can be seen from the log:
The 2021-11-29 14:08:42, 231 INFO [sports, 8481 ce2786b686fa, 8481 ce2786b686fa] [24916] [reactor-http-nio-2][com.github.jojotech.spring.cloud.apigateway.filter.CommonLogFilter:59]:POST -> http://127.0.0.1:8181/test-ss/anything? test=1: header: {" content-type: "[" text/plain"], "the user-agent:" [] "PostmanRuntime / 7.28.4", "Accept" : "* / *"], "Postman - Token" : [" 666 b17c9-0789-4 6 e6 - b515-9 a4538803308 "], "the Host" : [127.0.0.1:8181 ""]," Accept - Encoding ": [" gzip, deflate, Br] ", "Connection" : [" keep alive - "], "the content - length" : [8]} the 14:08:42 2021-11-29, 233 the DEBUG [sports,8481ce2786b686fa,8481ce2786b686fa] [24916] [reactor-http-nio-2][com.github.jojotech.spring.cloud.apigateway.filter.CommonLogFilter:74]:body: Ifasdasd 2021-11-29 14:08:42,463 INFO [Sports,,] [24916] [reactor-http-nio-2][com.github.jojotech.spring.cloud.apigateway.filter.CommonLogFilter$1:96]:response: POST - > http://127.0.0.1:8181/test-ss/anything? test=1 200 OK header: {"traceId":["8481ce2786b686fa"],"spanId":["8481ce2786b686fa"],"Date":["Mon, 29 Nov 2021 14:08:43 GMT], "" the content-type:" [] "application/json", "Server" : [" gunicorn / 19.9.0 "], "Access - Control - Allow - Origin: /" * ", "" Access - Contr ol-Allow-Credentials":["true"],"content-length":["886"]}, time: 232 ms 14:08:42 2021-11-29, 466 the DEBUG [sports, 8481 ce2786b686fa, 8481 ce2786b686fa] [24916] [reactor-http-nio-2][com.github.jojotech.spring.cloud.apigateway.filter.CommonLogFilter$1:105]:response: body: { "args": { "test": "1" }, "data": "ifasdasd", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate, br", "Content-Length": "8", "Content-Type": "text/plain", "Forwarded": "proto=http; The host = \ "127.0.0.1:8181 \"; For = "127.0.0.1:57526\", "Host": "httpbin.org", "postman-token ": "666b17c9-0789-46e6-b515-9a4538803308"," user-agent ": "PostmanRuntime / 7.28.4", "X - Amzn - Trace - Id" : "a4deeb Root = 1-61-3 d016ff729306d862edcca0b", "X - B3 - Parentspanid" : "8481ce2786b686fa", "X-B3-Sampled": "0", "X-B3-Spanid": "5def545b28a7a842", "X-B3-Traceid": X-forwarded-host: "127.0.0.1/8181 "," X-Forwarded-prefix ": "/test-ss"}, "JSON ": null, "method": "POST" and "origin", "127.0.0.1, 61.244.202.46", "url" : "Http://127.0.0.1:8181/anything?test=1"} 2021-11-29 14:08:42, 474 INFO [sports,,] the [24916] [[reactor - HTTP - nio - 2] reactor. Util. Loggers $Slf4JLogger: 269] : 8481 ce2786b686fa, 8481 ce2786b686fa - > 127.0.0.1: - 57526 [2021-11-29T14:08:42.230008z [Etc/GMT]] "POST /test-ss/anything? Test =1 HTTP/1.1" 200 886 243 msCopy the code
Wechat search “my programming meow” public account, a daily brush, easy to improve skills, won a variety of offers