开发者社区 问答 正文

在WebFlux中格式化Wiretap输出

我按照https://www.baeldung.com/spring-log-webclient-calls进行操作,最终得到以下代码来格式化Netty的HttpClient转储的日志

public class HttpLoggingHandler extends LoggingHandler {

    public HttpLoggingHandler(Class<?> clazz) {
        super(clazz);
    }

    @Override
    protected String format(ChannelHandlerContext ctx, String event, Object arg) {
        if (arg instanceof ByteBuf) {
            ByteBuf msg = (ByteBuf) arg;
            String output = msg.toString(StandardCharsets.UTF_8);
            return output;
        }

        return super.format(ctx, event, arg);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelReadComplete();
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelRegistered();
    }

    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelUnregistered();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelInactive();
    }

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

}

我最终得到了以下日志输出。

2019-12-26 15:14:17.450 DEBUG 30176 --- [ctor-http-nio-7] reactor.netty.http.client.HttpClient     : [id: 0x3638d952] CONNECT: localhost/127.0.0.1:8082
2019-12-26 15:14:17.455 DEBUG 30176 --- [ctor-http-nio-7] r.netty.http.client.HttpClientConnect    : [id: 0x3638d952, L:/127.0.0.1:61250 - R:localhost/127.0.0.1:8082] Handler is being applied: {uri=http://localhost:8082/enhanceAndSendForProcessing/, method=POST}
2019-12-26 15:14:17.477 DEBUG 30176 --- [ctor-http-nio-7] reactor.netty.http.client.HttpClient     : POST /enhanceAndSendForProcessing/ HTTP/1.1
host: localhost:8082
Accept: application/json
User-Agent: I'm a teapot
Content-Type: application/json
cookie: cookieKey=cookieValue
cookie: cookieKey=teapot
cookie: cookieKey=amsterdam
cookie: secretToken=f9a68c54-1242-40da-ad08-cce575fad42c
content-length: 96

{"id":4,"productId":4,"customer":{"firstName":"John","lastName":"Doe","email":"john@gmail.com"}}
2019-12-26 15:14:17.495 DEBUG 30176 --- [ctor-http-nio-7] reactor.netty.http.client.HttpClient     : HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1

4
2019-12-26 15:14:17.496 DEBUG 30176 --- [ctor-http-nio-7] r.n.http.client.HttpClientOperations     : [id: 0x3638d952, L:/127.0.0.1:61250 - R:localhost/127.0.0.1:8082] Received response (auto-read:false) : [Content-Type=application/json, Content-Length=1]
2019-12-26 15:14:17.506 DEBUG 30176 --- [ctor-http-nio-7] r.n.http.client.HttpClientOperations     : [id: 0x3638d952, L:/127.0.0.1:61250 - R:localhost/127.0.0.1:8082] Received last HTTP packet

这是对十六进制转储的巨大改进,但是有没有办法识别标题/ cookie和请求/响应主体?我看到请求日志的最后一行是请求正文

{"id":4,"productId":4,"customer":{"firstName":"John","lastName":"Doe","email":"john@gmail.com"}}

但有时请求主体会像这样单独转储:

2019-12-26 15:14:17.495 DEBUG 30176 --- [ctor-http-nio-7] reactor.netty.http.client.HttpClient     : {"id":4,"productId":4,"customer"{"firstName":"John","lastName":"Doe","email":"john@gmail.com"}}

我尝试arg以的身份进行检查instanceof HttpContent/HttpRequest,但没有运气。

我可以通过使用cookieBytebuf输出中的键来获取标头(使用HTTPHeaders映射)和cookie ,但是我不确定要获取请求/响应主体的方法,因为它以不同的顺序以不同的顺序打印。有办法解决吗?

展开
收起
垚tutu 2019-12-26 18:21:13 1131 分享 版权
阿里云 AI 助理回答

在你的自定义HttpLoggingHandler中,要更精确地识别和格式化请求头、Cookie以及请求/响应体,你可以利用Netty的内置对象结构。特别是对于请求头和Cookie,可以使用HttpRequestHttpHeaders;而对于请求/响应体,可以通过检查HttpContent来实现。

下面是一个改进的例子,展示了如何解析这些部分:

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.util.ReferenceCountUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EnhancedHttpLoggingHandler extends SimpleChannelInboundHandler<HttpObject> {

    private static final Logger log = LoggerFactory.getLogger(EnhancedHttpLoggingHandler.class);

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {
        if (msg instanceof HttpRequest) {
            HttpRequest request = (HttpRequest) msg;
            HttpHeaders headers = request.headers();
            // 打印请求行和请求头
            log.debug("HTTP Request: {} {}", request.method(), request.uri());
            headers.forEach(entry -> log.debug("Header: {}: {}", entry.getKey(), entry.getValue()));

            // 特别处理Cookie
            String cookies = headers.get(HttpHeaderNames.COOKIE);
            if (cookies != null) {
                log.debug("Cookies: {}", cookies);
            }
        } else if (msg instanceof HttpContent) {
            HttpContent content = (HttpContent) msg;
            ByteBuf contentBuffer = content.content();
            if (content instanceof LastHttpContent) {
                // 如果是最后一个内容块,打印请求/响应体
                String body = contentBuffer.toString(StandardCharsets.UTF_8);
                log.debug("Request/Response Body: {}", body);
                ReferenceCountUtil.release(contentBuffer); // 释放资源
            }
        }

        // 确保消息传递给下一个处理器
        ctx.fireChannelRead(msg);
    }

    // 其他需要重写的生命周期方法...
}

这个例子中,我们不再直接继承LoggingHandler,而是实现了SimpleChannelInboundHandler<HttpObject>,这样我们可以更精细地控制日志输出。通过判断传入的消息类型,我们可以分别处理HttpRequest(包括其头信息和Cookie)和HttpContent(包括请求或响应体)。注意,在处理完ByteBuf后,使用ReferenceCountUtil.release(contentBuffer)来确保资源被正确释放,防止内存泄漏。

请根据实际需求调整日志级别和格式,并确保此自定义处理器正确集成到你的Netty客户端或服务端配置中。

有帮助
无帮助
AI 助理回答生成答案可能存在不准确,仅供参考
0 条回答
写回答
取消 提交回答
问答分类:
问答地址: