SpringCloudGateway默认不打印请求和响应body,对于问题排查非常不友好。通过以下方式打印请求和响应body,header等内容。

实现自定义netty handler,转发请求和接受响应内容时打印请求内容。

package xxx

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.UUID;

@Slf4j
public class LoggingHandler extends ChannelDuplexHandler {

    private static final AttributeKey<String> TRACE_ID = AttributeKey.valueOf("traceId");

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        if (msg instanceof ByteBuf) {
            //定义uuid,用于关联请求和响应内容
            final String uuid = UUID.randomUUID().toString();
            if (!ctx.channel().hasAttr(TRACE_ID) || ctx.channel().attr(TRACE_ID).get().equals("")) {
                ctx.channel().attr(TRACE_ID).set(uuid);
            }
            final ByteBuf buf = (ByteBuf) msg;
            int length = buf.readableBytes();
            //设置MDC,打印日志时增加trace
            MDC.put("trace", ctx.channel().attr(TRACE_ID).get());
            if (length > 0) {
                final String content = buf.toString(StandardCharsets.UTF_8);
                final StringBuilder sb = new StringBuilder();
                Arrays.stream(content.split("\r\n|\n"))
                        .forEach(str -> sb.append(str).append("\n"));
                log.info("request meta: {}", sb.toString());
            }
        }
        super.write(ctx, msg, promise);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof ByteBuf) {
            if (ctx.channel().hasAttr(TRACE_ID)) {
                ctx.channel().attr(TRACE_ID).set("");
            }
            final ByteBuf buf = (ByteBuf) msg;
            int length = buf.readableBytes();
            if (length > 0) {
                String content = buf.toString(StandardCharsets.UTF_8);
                final StringBuilder sb = new StringBuilder(length);
                Arrays.stream(content.split("\r\n|\n"))
                        .forEach(str -> sb.append(str).append("\n"));
                log.info("response meta: {}", sb.toString());
            }
            //打印完日志后移除
            MDC.remove("trace");
        }
        super.channelRead(ctx, msg);
    }
}

配置HttpClient,增加自定义handler

@Component
public class TestHttpClientCustomizer implements HttpClientCustomizer {
    @Override
    public HttpClient customize(HttpClient client) {
        return client.tcpConfiguration(tcpClient ->
                tcpClient.bootstrap(b -> BootstrapHandlers.updateConfiguration(b, "log",
                        ((connectionObserver, channel) -> channel.pipeline().addFirst("log", new LoggingHandler())))));
    }
}

本人用的是logback,在logback配置文件内添加

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
                <!-- 添加 [%X{trace:-}] 打印日志时增加自定义的trace -->
                <pattern>%d{HH:mm:ss.SSS} [%X{trace:-}] [%thread] %-5level %logger - %msg %n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>

日志打印效果,日志内打印了请求内容,如果请求带有body,内容也会被打印。根据上述配置的uuid,请求和响应内容对应的日志,增加了唯一流水号,确保日志内请求和响应内容能够对应。

19:02:08.564 [bdd024fc-6bac-4b60-a1d6-8a402c39829f] [reactor-http-nio-4] INFO  com.laomei.test.gatewaytest.LoggingHandler - request meta: GET /healthz HTTP/1.1
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8
Forwarded: proto=http;host="localhost:9001";for="0:0:0:0:0:0:0:1:58012"
X-Forwarded-For: 0:0:0:0:0:0:0:1
X-Forwarded-Proto: http
X-Forwarded-Prefix: /READING-INSTANCE
X-Forwarded-Port: 9001
X-Forwarded-Host: localhost:9001
host: 127.0.0.1:9005
content-length: 0
 
19:02:08.573 [bdd024fc-6bac-4b60-a1d6-8a402c39829f] [reactor-http-nio-4] INFO  com.laomei.test.gatewaytest.LoggingHandler - response meta: HTTP/1.1 200 
Content-Type: text/html;charset=UTF-8
Content-Length: 15
Date: Mon, 29 Nov 2021 11:02:08 GMT

{"status":"UP"}
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐