我按照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 ,但是我不确定要获取请求/响应主体的方法,因为它以不同的顺序以不同的顺序打印。有办法解决吗?
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
在你的自定义HttpLoggingHandler
中,要更精确地识别和格式化请求头、Cookie以及请求/响应体,你可以利用Netty的内置对象结构。特别是对于请求头和Cookie,可以使用HttpRequest
和HttpHeaders
;而对于请求/响应体,可以通过检查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客户端或服务端配置中。