前言

我们在使用网关的时候,有时候客户端会有莫名其妙的问题需要服务端辅助定位,这时候有一份完全的请求的信息的日志会非常有帮助,这里提供一种基于过滤器的实现方式。

我的实现方式是作为一个工程来实现的,方便后续的引用

Gateway 代理日志记录 Filter_Windows


pom.xml依赖

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>

过滤器 实现类

package com.spacetime.gateway.recorder.config;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


@Component
@ConditionalOnProperty(value = "gateway.log.enable", matchIfMissing = true)
public class GatewayLoggerProfile {
public static final String LOGGER_NAME = "requestRecorder";

@Value("${logging.path}")
private String logPath;

//@Value("${logging.pattern.file}")
String logPattern = "%date %level [%thread] %logger{36} [%file : %line] %msg%n";

@PostConstruct
public void onReady() {
LoggerContext context = (LoggerContext)StaticLoggerBinder.getSingleton().getLoggerFactory();

//把 log 写入到专门的文件中
Logger logger = context.getLogger(LOGGER_NAME);
logger.detachAndStopAllAppenders();

String logFile = null;
if (logPath.endsWith("/"))
logFile = logPath + "pv/pv.log";
else
logFile = logPath + "/pv/pv.log";

//以下代码 通过反编译 log组件代码发现的, 不保证时效性
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>();
appender.setFile(logFile);

PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(logPattern);
encoder.setContext(context);
encoder.setParent(appender);
appender.setEncoder(encoder);
encoder.start();

TimeBasedRollingPolicy policy = new TimeBasedRollingPolicy();
policy.setCleanHistoryOnStart(false);
policy.setMaxHistory(30);
policy.setContext(context);
policy.setFileNamePattern(logFile + ".%d{yyyy-MM-dd}.gz");
policy.setParent(appender);
appender.setRollingPolicy(policy);
policy.start();

appender.setName(LOGGER_NAME + "-appender");
appender.setContext(context);
appender.start();

logger.addAppender(appender);

logger.setAdditive(false);
}
}

辅助类 RecorderServerHttpRequestDecorator

package com.spacetime.gateway.recorder.filter;

import com.spacetime.gateway.recorder.filter.util.DataBufferUtilFix;
import com.spacetime.gateway.recorder.filter.util.DataBufferWrapper;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class RecorderServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private DataBufferWrapper data = null;

public RecorderServerHttpRequestDecorator(ServerHttpRequest delegate) {
super(delegate);
}

@Override
public Flux<DataBuffer> getBody() {
synchronized (this) {
Mono<DataBuffer> mono = null;
if (data == null) {
mono = DataBufferUtilFix.join(super.getBody())
.doOnNext(d -> this.data = d)
.filter(d -> d.getFactory() != null)
.map(DataBufferWrapper::newDataBuffer);
} else {
mono = Mono.justOrEmpty(data.newDataBuffer());
}

return Flux.from(mono);
}
}
}
package com.spacetime.gateway.recorder.filter;


import com.spacetime.gateway.recorder.filter.util.DataBufferUtilFix;
import com.spacetime.gateway.recorder.filter.util.DataBufferWrapper;
import org.reactivestreams.Publisher;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public class RecorderServerHttpResponseDecorator extends ServerHttpResponseDecorator {
private DataBufferWrapper data = null;

public RecorderServerHttpResponseDecorator(ServerHttpResponse delegate) {
super(delegate);
}

@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
return DataBufferUtilFix.join(Flux.from(body))
.doOnNext(d -> this.data = d)
.flatMap(d -> super.writeWith(copy()));
}

@Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return writeWith(Flux.from(body)
.flatMapSequential(p -> p));
}

public Flux<DataBuffer> copy() {
//如果data为null 就出错了 正好可以调试
DataBuffer buffer = this.data.newDataBuffer();
if (buffer == null)
return Flux.empty();

return Flux.just(buffer);
}
}

实现的效果如下:

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:39 GMT
------------body------------
1212
------------ end ------------





2022-10-21 22:47:40,235 INFO [reactor-http-nio-6] requestRecorder [LowerRequestRecorderGlobalFilter.java : 57]
---------------------------
原始请求:
GET http://127.0.0.1:8100/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:39 GMT
------------body------------
1212
------------ end ------------





2022-10-21 22:47:40,421 INFO [reactor-http-nio-6] requestRecorder [LowerRequestRecorderGlobalFilter.java : 57]
---------------------------
原始请求:
GET http://127.0.0.1:8100/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:39 GMT
------------body------------
1212
------------ end ------------





2022-10-21 22:47:40,594 INFO [reactor-http-nio-6] requestRecorder [LowerRequestRecorderGlobalFilter.java : 57]
---------------------------
原始请求:
GET http://127.0.0.1:8100/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:40 GMT
------------body------------
1212
------------ end ------------





2022-10-21 22:47:40,949 INFO [reactor-http-nio-6] requestRecorder [LowerRequestRecorderGlobalFilter.java : 57]
---------------------------
原始请求:
GET http://127.0.0.1:8100/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:40 GMT
------------body------------
1212
------------ end ------------





2022-10-21 22:47:41,106 INFO [reactor-http-nio-6] requestRecorder [LowerRequestRecorderGlobalFilter.java : 57]
---------------------------
原始请求:
GET http://127.0.0.1:8100/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

代理请求:
GET http://192.168.30.1:8210/usermgr/user/id
------------请求头------------
Host:127.0.0.1:8100
Connection:keep-alive
Cache-Control:max-age=0
sec-ch-ua:"Chromium";v="106", "Google Chrome";v="106", "Not;A=Brand";v="99"
sec-ch-ua-mobile:?0
sec-ch-ua-platform:"Windows"
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36
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:zh-CN,zh;q=0.9
------------ end ------------

响应:200 OK
------------响应头------------
transfer-encoding:chunked
Content-Type:application/json
Date:Fri, 21 Oct 2022 14:47:40 GMT
------------body------------
1212
------------ end ------------