参考: www.borischistov.com/articles/2 github.com/BorisChisto…
package demo.uyoung.example.gateway.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.dtyunxi.app.ServiceContext;
import com.dtyunxi.util.DateUtil;
import demo.uyoung.example.gateway.util.NetworkUtil;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.PooledDataBuffer;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* 请求日志记录
*
* @date 2021.06.03
*/
@Component
public class LoggingFilter implements GlobalFilter, Ordered {
public static final String REQID_KEY = "request_id";
private static final Set<String> LOGGABLE_CONTENT_TYPES = new HashSet<>(Arrays.asList(
MediaType.APPLICATION_JSON_VALUE.toLowerCase(),
MediaType.APPLICATION_JSON_UTF8_VALUE.toLowerCase(),
MediaType.TEXT_PLAIN_VALUE,
MediaType.TEXT_XML_VALUE
));
@Value("${support-datagram-record:false}")
private boolean supportDatagramRecord = false;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// First we get here request in exchange
ServerHttpRequestDecorator requestMutated = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
MonoLogger requestLogger = new MonoLogger(getDelegate(), supportDatagramRecord, exchange);
if (LOGGABLE_CONTENT_TYPES.contains(String.valueOf(getHeaders().getContentType()).toLowerCase())) {
return super.getBody().map(ds -> {
requestLogger.appendBody(ds.asByteBuffer());
return ds;
}).doFinally((s) -> requestLogger.log());
} else {
requestLogger.log();
return super.getBody();
}
}
};
ServerHttpResponseDecorator responseMutated = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
MonoLogger responseLogger = new MonoLogger(getDelegate());
if (LOGGABLE_CONTENT_TYPES.contains(String.valueOf(getHeaders().getContentType()).toLowerCase())) {
return join(body).flatMap(db -> {
//不记录响应就不需要这几行
//responseLogger.appendBody(db.asByteBuffer());
//responseLogger.log();
return getDelegate().writeWith(Mono.just(db));
});
} else {
responseLogger.log();
return getDelegate().writeWith(body);
}
}
};
return chain.filter(exchange.mutate().request(requestMutated).response(responseMutated).build());
}
private Mono<? extends DataBuffer> join(Publisher<? extends DataBuffer> dataBuffers) {
Assert.notNull(dataBuffers, "'dataBuffers' must not be null");
return Flux.from(dataBuffers)
.collectList()
.filter((list) -> !list.isEmpty())
.map((list) -> list.get(0).factory().join(list))
.doOnDiscard(PooledDataBuffer.class, DataBufferUtils::release);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
private static class MonoLogger {
//这个logger用来记录当前
private static final org.slf4j.Logger log = LoggerFactory.getLogger(LoggingFilter.class);
//这个是用来替换 demo.uyoung.example.gateway.filter.RequestRecordFilter 的记录
private static final org.slf4j.Logger reqLog = LoggerFactory.getLogger("demo.uyoung.example.gateway.filter.RequestRecordFilterFake");
private StringBuilder sb = new StringBuilder();
private CharBuffer body;
private boolean supportDatagramRecord;
private Map reqRecord;
MonoLogger(ServerHttpResponse response) {
/* sb.append("\n");
sb.append("---- Response -----").append("\n");
sb.append("Headers :").append(response.getHeaders().toSingleValueMap()).append("\n");
sb.append("Status code :").append(response.getStatusCode()).append("\n");*/
}
MonoLogger(ServerHttpRequest request, boolean supportDatagramRecord, ServerWebExchange exchange) {
/*sb.append("\n");
sb.append("---- Request -----").append("\n");
sb.append("Headers :").append(request.getHeaders().toSingleValueMap()).append("\n");
sb.append("Method :").append(request.getMethod()).append("\n");
sb.append("Client :").append(request.getRemoteAddress()).append("\n");*/
String url = request.getURI().toString();
sb.append("开始发送请求:").append(url).append("\n");
if (supportDatagramRecord) {
this.supportDatagramRecord = supportDatagramRecord;
reqRecord = saveBody(request, exchange);
}
}
void appendBody(ByteBuffer byteBuffer) {
// sb.append("Body :").append(StandardCharsets.UTF_8.decode(byteBuffer)).append("\n");
this.body = StandardCharsets.UTF_8.decode(byteBuffer);
}
void log() {
/*sb.append("-------------------").append("\n");*/
if (sb.length() > 0) {
log.info(sb.toString());
} else {
log.debug("空内容");
}
if (supportDatagramRecord) {
String content;
if (reqRecord == null || reqRecord.isEmpty()) {
content = "{}";
} else {
content = JSON.toJSONString(reqRecord, SerializerFeature.WriteMapNullValue);
}
reqLog.info("request record:{}", content);
}
}
private HashMap<String, Object> saveBody(ServerHttpRequest request, ServerWebExchange exchange) {
if (exchange == null) {
log.debug("exchange 为 null,跳过");
return null;
}
//exchange.getAttributes().put(REQBODY_KEY, body);
String requestId = ServiceContext.getContext().getRequestId();
if (StringUtils.isBlank(requestId)) {
requestId = UUID.randomUUID().toString().replaceAll("-", "");
}
// 排除GET和文件上传的情况
if (exchange.getAttributes() != null) {
exchange.getAttributes().put(REQID_KEY, requestId);
}
HttpMethod method = exchange.getRequest().getMethod();
HttpHeaders headers = exchange.getRequest().getHeaders();
//ServerHttpRequest request = exchange.getRequest();
MultiValueMap<String, HttpCookie> cookies = request.getCookies();
String uri = request.getURI().toString();
MultiValueMap<String, String> queryParams = request.getQueryParams();
HashMap<String, Object> reqRecord = new HashMap<>();
// 获取真实ip
String ip = NetworkUtil.getIpAddr(request);
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
reqRecord.put("routerName", route.getId());
reqRecord.put("requestId", requestId);
reqRecord.put("headers", headers.toSingleValueMap());
reqRecord.put("cookies", cookies.toSingleValueMap());
reqRecord.put("uri", uri);
reqRecord.put("requestBody", body);
reqRecord.put("method", method);
reqRecord.put("remoteAddress", ip);
reqRecord.put("queryParams", queryParams.toSingleValueMap());
reqRecord.put("timestamp", DateUtil.getCurrentTimeNum());
return reqRecord;
}
}
}