iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >gateway、webflux、reactor-netty请求日志输出方式
  • 923
分享到

gateway、webflux、reactor-netty请求日志输出方式

2024-04-02 19:04:59 923人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录gateway、WEBflux、Reactor-Netty请求日志输出场景思路解决方案spring-webflux、gateway、SpringBoot-start-web问题S

gateway、webflux、reactor-netty请求日志输出

场景

在使用spring cloud gateway时想要输出请求日志,考虑到两种实现方案

方案一

官网中使用Reactor Netty Access Logs方案,配置“-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;
 

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中的方法进行重写,覆盖请求的整个生命周期。

spring-webflux、gateway、springboot-start-web问题

Spring-webflux

当两者一起时配置的并不是webflux web application, 仍然时一个spring mvc web application。

官方文档中有这么一段注解:

很多开发者添加spring-boot-start-webflux到他们的spring mvc web applicaiton去是为了使用reactive WebClient. 如果希望更改webApplication 类型需要显示的设置,如SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE).

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

结论一:

当两者一起时配置的并不是webflux web application, 仍然时一个spring mvc web application。但是启动不会报错,可以正常使用,但是webflux功能失效

Spring-gateway

因为gateway和zuul不一样,gateway用的是长连接,netty-webflux,zuul1.0用的就是同步webmvc。

所以你的非gateway子项目启动用的是webmvc,你的gateway启动用的是webflux. spring-boot-start-web和spring-boot-start-webflux相见分外眼红。

不能配置在同一pom.xml,或者不能在同一项目中出现,不然就会启动报错

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

结论二:

当spring-cloud-gateway和spring-boot-starer-web两者一起时配置的时候, 启动直接报错,依赖包冲突不兼容

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: gateway、webflux、reactor-netty请求日志输出方式

本文链接: https://www.lsjlt.com/news/141892.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • gateway、webflux、reactor-netty请求日志输出方式
    目录gateway、webflux、reactor-netty请求日志输出场景思路解决方案spring-webflux、gateway、springboot-start-web问题S...
    99+
    2024-04-02
  • gateway、webflux、reactor-netty请求日志输出的方式是什么
    本篇内容介绍了“gateway、webflux、reactor-netty请求日志输出的方式是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所...
    99+
    2023-06-29
  • SpringCloud Gateway之请求应答日志打印方式
    目录Gateway请求应答日志打印第一步第二步Gateway全局请求日志打印把请求体的数据存入exchange编写全局日志拦截器代码在代码中配置全局拦截器Gateway请求应答日志打...
    99+
    2024-04-02
  • Springboot异常日志输出方式
    目录lombok插件使用统一异常处理统一日志输出配置日志级别↓Logback日志↓配置logback日志↓安装idea彩色日志插件:grep-console复制粘贴即可lombok插...
    99+
    2024-04-02
  • logback日志输出格式设置方式
    目录部分标签解释内置转换器部分特殊字符串解释更多内置特殊字符如何自定义输出样式字符颜色定义内置的一些样式代码示例使用内置模板使用自定义模板总结部分标签解释 withJansi: 是否...
    99+
    2023-05-14
    logback日志输出 logback日志格式设置 logback日志
  • Springboot允许logger.debug输出日志方式
    目录Springboot允许logger.debug输出日志SpringBoot启动debug级别日志报错错误截图原因解决办法Springboot允许logger.debug输出日志...
    99+
    2024-04-02
  • logback输出日志屏蔽quartz的debug等级日志方式
    目录logback输出日志屏蔽quartz的debug等级日志解决方法quartz关闭DBUG日志把他的log级别改成 ERROR或者索性logback输出日志屏蔽quartz的de...
    99+
    2024-04-02
  • Slf4j+logback实现JSON格式日志输出方式
    目录Slf4j+logback实现JSON格式日志输出依赖logback 记录JSON日志Slf4j+logback实现JSON格式日志输出 依赖 <dependency&...
    99+
    2024-04-02
  • log4j2异步日志输出方式有几种
    小编给大家分享一下log4j2异步日志输出方式有几种,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!log4j2的异步日志输出方式使用log4j2的同步日志进行日志...
    99+
    2023-06-22
  • 关于log4j2的异步日志输出方式
    目录log4j2的异步日志输出方式第一种实现异步方式AsyncAppender第二种实现异步方式AsyncLoggerlog4j2异步注意事项log4j2异步类型小提示log4j2的...
    99+
    2024-04-02
  • Feign的请求和响应日志方式是什么
    这篇文章主要介绍了Feign的请求和响应日志方式是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Feign的请求和响应日志方式是什么文章都会有所收获,下面我们一起来看看吧。1、项目里定义FeignClien...
    99+
    2023-07-02
  • 全局记录Feign的请求和响应日志方式
    目录1、项目里定义FeignClient接口2、单个FeignClient接口开启日志3、所有FeignClient接口 开启日志3.1、修改FeignConfiguration3....
    99+
    2024-04-02
  • Springboot异常日志输出方式的示例分析
    小编给大家分享一下Springboot异常日志输出方式的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!lombok插件使用引入依赖,在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/sette...
    99+
    2023-06-22
  • Slf4j+logback实现JSON格式日志输出方式是什么
    Slf4j+logback实现JSON格式日志输出方式是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Slf4j+logback实现JSON格式日志输出依赖<de...
    99+
    2023-06-22
  • Spring Boot mybatis-config 和 log4j 输出sql 日志的方式
    依赖 <dependency> <groupId>log4j</groupId> <a...
    99+
    2024-04-02
  • Golang GinWeb之自定义日志格式和输出方式/启禁日志颜色的方法是什么
    这篇文章主要介绍“Golang GinWeb之自定义日志格式和输出方式/启禁日志颜色的方法是什么”,在日常操作中,相信很多人在Golang GinWeb之自定义日志格式和输出方式/启禁日志颜色的方法是什么问...
    99+
    2024-04-02
  • SpringMVC框架中使用Filter实现请求日志打印方式
    目录查找资料后确定两种技术方案具体实现总结一下之前利用HttpServletRequest.getInputStream()和RequestWrapper实现了请求的requestB...
    99+
    2024-04-02
  • log4j控制日志输出文件名称的两种方式小结
    目录log4j控制日志输出文件名称1. 第一种方式2. 第二种方式(这种方式亲测正确)如何随心所欲地自定义log4j输出格式log4j控制日志输出文件名称 1. 第一种方式 在类对象...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作