gateway、webflux、reactor-netty请求日志输出
发布日期:2021-06-30 21:31:37
浏览次数:2
分类:技术文章
本文共 5391 字,大约阅读时间需要 17 分钟。
场景
在使用spring cloud gateway时想要输出请求日志,考虑到两种实现方案
方案一
官网中使用方案,配置“-Dreactor.netty.http.server.accessLogEnabled=true”开启日志记录。
输出如下:
reactor.netty.http.server.AccessLog :10.2.20.177 - - [02/Dec/2020:16:41:57 +0800] "GET /fapi/gw/hi/login HTTP/1.1" 200 319 8080 626 ms
- 优点:简单方便
- 缺点:格式固定,信息量少
方案二
创建一个logfilter,在logfilter中解析request,并输出请求信息
- 优点:可以自定义日志格式和内容,可以获取body信息
- 缺点:返回信息需要再写一个filter,没有匹配到路由时无法进入到logfilter中
思路
对方案一进行改造,使其满足需求。对reactor-netty源码分析,主要涉及
- AccessLog:日志工具,日志结构体
- AccessLogHandler:http1.1协议日志控制,我们主要使用这个。
- AccessLogHandler2:http2协议日志控制
代码如下:
package reactor.netty.http.server;import reactor.util.Logger;import reactor.util.Loggers;import java.time.ZonedDateTime;import java.time.format.DateTimeFormatter;import java.util.Locale;import java.util.Objects;final class AccessLog { static final Logger log = Loggers.getLogger("reactor.netty.http.server.AccessLog"); static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("dd/MMM/yyyy:HH:mm:ss Z", Locale.US); static final String COMMON_LOG_FORMAT = "{} - {} [{}] \"{} {} {}\" {} {} {} {} ms"; static final String MISSING = "-"; final String zonedDateTime; String address; CharSequence method; CharSequence uri; String protocol; String user = MISSING; CharSequence status; long contentLength; boolean chunked; long startTime = System.currentTimeMillis(); int port; AccessLog() { this.zonedDateTime = ZonedDateTime.now().format(DATE_TIME_FORMATTER); } AccessLog address(String address) { this.address = Objects.requireNonNull(address, "address"); return this; } AccessLog port(int port) { this.port = port; return this; } AccessLog method(CharSequence method) { this.method = Objects.requireNonNull(method, "method"); return this; } AccessLog uri(CharSequence uri) { this.uri = Objects.requireNonNull(uri, "uri"); return this; } AccessLog protocol(String protocol) { this.protocol = Objects.requireNonNull(protocol, "protocol"); return this; } AccessLog status(CharSequence status) { this.status = Objects.requireNonNull(status, "status"); return this; } AccessLog contentLength(long contentLength) { this.contentLength = contentLength; return this; } AccessLog increaseContentLength(long contentLength) { if (chunked) { this.contentLength += contentLength; } return this; } AccessLog chunked(boolean chunked) { this.chunked = chunked; return this; } long duration() { return System.currentTimeMillis() - startTime; } void log() { if (log.isInfoEnabled()) { log.info(COMMON_LOG_FORMAT, address, user, zonedDateTime, method, uri, protocol, status, (contentLength > -1 ? contentLength : MISSING), port, duration()); } }}
- AccessLogHandler:日志控制
package reactor.netty.http.server;import io.netty.buffer.ByteBuf;import io.netty.buffer.ByteBufHolder;import io.netty.channel.ChannelDuplexHandler;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelPromise;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.http.HttpRequest;import io.netty.handler.codec.http.HttpResponse;import io.netty.handler.codec.http.HttpResponseStatus;import io.netty.handler.codec.http.HttpUtil;import io.netty.handler.codec.http.LastHttpContent;/** * @author Violeta Georgieva */final class AccessLogHandler extends ChannelDuplexHandler { AccessLog accessLog = new AccessLog(); @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (msg instanceof HttpRequest) { final HttpRequest request = (HttpRequest) msg; final SocketChannel channel = (SocketChannel) ctx.channel(); accessLog = new AccessLog() .address(channel.remoteAddress().getHostString()) .port(channel.localAddress().getPort()) .method(request.method().name()) .uri(request.uri()) .protocol(request.protocolVersion().text()); } ctx.fireChannelRead(msg); } @Override @SuppressWarnings("FutureReturnValueIgnored") public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { if (msg instanceof HttpResponse) { final HttpResponse response = (HttpResponse) msg; final HttpResponseStatus status = response.status(); if (status.equals(HttpResponseStatus.CONTINUE)) { //"FutureReturnValueIgnored" this is deliberate ctx.write(msg, promise); return; } final boolean chunked = HttpUtil.isTransferEncodingChunked(response); accessLog.status(status.codeAsText()) .chunked(chunked); if (!chunked) { accessLog.contentLength(HttpUtil.getContentLength(response, -1)); } } if (msg instanceof LastHttpContent) { accessLog.increaseContentLength(((LastHttpContent) msg).content().readableBytes()); ctx.write(msg, promise.unvoid()) .addListener(future -> { if (future.isSuccess()) { accessLog.log(); } }); return; } if (msg instanceof ByteBuf) { accessLog.increaseContentLength(((ByteBuf) msg).readableBytes()); } if (msg instanceof ByteBufHolder) { accessLog.increaseContentLength(((ByteBufHolder) msg).content().readableBytes()); } //"FutureReturnValueIgnored" this is deliberate ctx.write(msg, promise); }}
- 执行顺序
AccessLogHandler.channelRead > GlobalFilter.filter > AbstractLoadBalance.choose >response.writeWith >AccessLogHandler.write
解决方案
对AccessLog和AccessLogHandler进行重写,输出自己想要的内容和样式。
AccessLogHandler中重写了ChannelDuplexHandler中的channelRead和write方法,还可以对ChannelInboundHandler和ChannelOutboundHandler中的方法进行重写,覆盖请求的整个生命周期。
转载地址:https://lizz6.blog.csdn.net/article/details/110489737 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
很好
[***.229.124.182]2024年04月17日 09时07分38秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
为什么执行 x in range(y) 如此快?
2019-05-01
看完这篇文章你还不理解 Python 装饰器,只有一种可能...
2019-05-01
Python干货:表达式 i += x 与 i = i + x 等价吗?
2019-05-01
有了这些 Chrome 插件,效率提升10倍(建议收藏)
2019-05-01
Python 编码错误的本质原因
2019-05-01
Python 开发者都会遇到的错误:UnboundLocalError
2019-05-01
用 Python 送“爱心”
2019-05-01
理解HTTPS为什么安全前,先看看这些东西
2019-05-01
最通俗易懂地解释:正向代理与反向代理
2019-05-01
代码这样写不止于优雅(Python版)
2019-05-01
一份来自掘金社区的开发者报告
2019-05-01
一本书搞定Python入门到实践
2019-05-01
20-40k+期权,蚂蚁金服招 Python 开发工程师
2019-05-01
2017半年文章合集(建议收藏)
2019-05-01
简析 __init__、__new__、__call__ 方法
2019-05-01
程序员如何优雅地写公众号
2019-05-01
@classmethod与@staticmethod的区别
2019-05-01
只有1%的程序员搞懂过浮点数陷阱
2019-05-01
一个网络请求的历险之旅
2019-05-01
Google 为什么把几十亿行代码放在一个库
2019-05-01