上面的核心代码是:
// 重写原始请求 ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(reqBody.getBytes()); return Flux.just(bodyDataBuffer); } }; return chain.filter(exchange.mutate() .request(requestDecorator) .build()).then(LogHelper.doRecord(logDTO));如果不需要对session进行操作,可以直接调用这块就行。
关于请求时间,我这里采用的是将时间戳放进请求头中,等到打印日志的时候再从请求头中读取然后计算出时间。否则如果单独在某个filter中计算请求时间,会造成时间不太准确。当然这样时间也不是很准确,毕竟还有Spring本身的filter等业务逻辑,不过时间相差不是很大,大概十几毫秒的样子。
第二种第二种就是自己缓存下请求体,读取的时候读取缓存内容。
代码如下:
package com.lifengdi.gateway.log; import com.lifengdi.gateway.constant.HeaderConstant; import com.lifengdi.gateway.utils.IpUtils; import io.netty.buffer.UnpooledByteBufAllocator; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.NettyDataBufferFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import reactor.core.publisher.Flux; import reactor.core.scheduler.Schedulers; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Objects; /** * 对ServerHttpRequest进行二次封装,解决requestBody只能读取一次的问题 * @author: Li Fengdi * @date: 2020-03-17 18:02 */ @Slf4j public class CacheServerHttpRequestDecorator extends ServerHttpRequestDecorator { private DataBuffer bodyDataBuffer; private int getBufferTime = 0; private byte[] bytes; public CacheServerHttpRequestDecorator(ServerHttpRequest delegate) { super(delegate); } @Override public Flux<DataBuffer> getBody() { if (getBufferTime == 0) { getBufferTime++; Flux<DataBuffer> flux = super.getBody(); return flux.publishOn(Schedulers.single()) .map(this::cache) .doOnComplete(() -> trace(getDelegate())); } else { return Flux.just(getBodyMore()); } } private DataBuffer getBodyMore() { NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); bodyDataBuffer = nettyDataBufferFactory.wrap(bytes); return bodyDataBuffer; } private DataBuffer cache(DataBuffer buffer) { try { InputStream dataBuffer = buffer.asInputStream(); bytes = IOUtils.toByteArray(dataBuffer); NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); bodyDataBuffer = nettyDataBufferFactory.wrap(bytes); return bodyDataBuffer; } catch (IOException e) { e.printStackTrace(); } return null; } private void trace(ServerHttpRequest request) { URI requestUri = request.getURI(); String uriQuery = requestUri.getQuery(); String url = requestUri.getPath() + (StringUtils.isNotBlank(uriQuery) ? "?" + uriQuery : ""); HttpHeaders headers = request.getHeaders(); MediaType mediaType = headers.getContentType(); String schema = requestUri.getScheme(); String method = request.getMethodValue().toUpperCase(); if ((!"http".equals(schema) && !"https".equals(schema))) { return; } String reqBody = null; if (Objects.nonNull(mediaType) && LogHelper.isUploadFile(mediaType)) { reqBody = "上传文件"; } else { if (method.equals("GET")) { if (StringUtils.isNotBlank(uriQuery)) { reqBody = uriQuery; } } else if (headers.getContentLength() > 0) { reqBody = LogHelper.readRequestBody(request); } } final Log logDTO = new Log(); logDTO.setLevel(Log.LEVEL.INFO); logDTO.setRequestUrl(url); logDTO.setRequestBody(reqBody); logDTO.setRequestMethod(method); logDTO.setRequestId(headers.getFirst(HeaderConstant.REQUEST_ID)); logDTO.setIp(IpUtils.getClientIp(request)); log.info(LogHelper.toJsonString(logDTO)); } }filter这里就简单写下:
package com.lifengdi.gateway.filter; import com.lifengdi.gateway.constant.OrderedConstant; import com.lifengdi.gateway.log.CacheServerHttpRequestDecorator; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * @author: Li Fengdi * @date: 2020-03-17 18:17 */ //@Component public class LogFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { CacheServerHttpRequestDecorator cacheServerHttpRequestDecorator = new CacheServerHttpRequestDecorator(exchange.getRequest()); return chain.filter(exchange.mutate().request(cacheServerHttpRequestDecorator).build()); } @Override public int getOrder() { return OrderedConstant.LOGGING_FILTER; } }