Spring Cloud Gateway 记录访问日志
记录日志时通常关注请求URI、IP、Method、QueryString、POST、Header等。 对于Spring Cloud Gateway这其中的POST请求的Body获取比较复杂, request body是只能读取一次的,如果读取以后不封装回去,则会造成后面的服务无法读取body数据。
方案一:
添加一个全局过滤器预先获取并存入请求的Attributes中。
/**
* @program: GeekOS
* @description: 存入Body
* @author: 轩辕子墨
* @create: 2019-09-18 23:22
**/
@Component
public class CacheBodyFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue();
if(!("GET".equalsIgnoreCase(method) || "DELETE".equalsIgnoreCase(method))) {
ServerRequest serverRequest = new DefaultServerRequest(exchange);
Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
return bodyToMono.flatMap(body -> {
exchange.getAttributes().put("cachedRequestBody", body);
ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
return Flux.just(bodyDataBuffer);
}
};
return chain.filter(exchange.mutate().request(newRequest).build());
});
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -99;
}
}
2:创建记录日志的过滤器
@Slf4j
@Component
public class LogFilter implements GlobalFilter, Ordered {
private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final SimpleDateFormat DateFormat = new SimpleDateFormat("yyyy-MM-dd-HH");
@Data
static class LogInfo{
String method;
String body;
String Params;
String url;
String header;
String result;
String ip;
long timestamp = System.currentTimeMillis();
String date = simpleDateFormat.format(new Date());
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
LogInfo logInfo = new LogInfo();
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String method = serverHttpRequest.getMethodValue().toUpperCase();
logInfo.setMethod(method);
logInfo.setHeader(serverHttpRequest.getHeaders().toString());
logInfo.setUrl(serverHttpRequest.getURI().toString());
if(StringUtils.isNotBlank(serverHttpRequest.getHeaders().getFirst("X-Real-IP")))
logInfo.setIp(serverHttpRequest.getHeaders().getFirst("X-Real-IP"));
else
logInfo.setIp("IP获取异常!");
if(!("GET".equals(method) || "DELETE".equals(method))) {
String body = exchange.getAttributeOrDefault("cachedRequestBody", "");
if(StringUtils.isNotBlank(body)) {
logInfo.setBody(body);
}
}
ServerHttpResponse serverHttpResponse = exchange.getResponse();
DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);
String resp = new String(content, StandardCharsets.UTF_8);
logInfo.setResult(resp);
new Thread(()->{writeAccessLog(logInfo);}).start();
byte[] uppedContent = new String(content, StandardCharsets.UTF_8).getBytes();
return bufferFactory.wrap(uppedContent);
}));
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
@Override
public int getOrder() {
return -20;
}
@Async
public void writeAccessLog(LogInfo logInfo){
File file = new File("gateway-log"+File.separator+DateFormat.format(new Date())+".log");
if (!file.exists()){
try {
if (file.createNewFile()){
file.setWritable(true);
}
} catch (IOException e) {
log.error("创建访问日志文件失败.{}",e.getMessage(),e);
}
}
try(FileWriter fileWriter = new FileWriter(file,true)){
fileWriter.write( JSONObject.toJSONString(logInfo) + "\r\n");
} catch (FileNotFoundException e){
log.error("文件不存在,请检查文件. {}", e.getMessage(),e);
} catch (IOException e) {
log.error("写访问日志到文件失败. {}", e.getMessage(),e);
}
}
}
效果预览
方案二
原文引自:https://www.cnblogs.com/westlin/p/10960251.html
/**
* @program: GeekOS
* @description: 日志记录
* @author: 轩辕子墨
* @create: 2019-09-18 23:09
**/
@Slf4j
@Component
public class RequestRecordFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取用户传来的数据类型
MediaType mediaType = exchange.getRequest().getHeaders().getContentType();
ServerRequest serverRequest = new DefaultServerRequest(exchange);
// 如果是json格式,将body内容转化为object or map 都可
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)){
Mono<Object> modifiedBody = serverRequest.bodyToMono(Object.class).flatMap(body -> {
recordLog(exchange.getRequest(), body);
return Mono.just(body);
});
return getVoidMono(exchange, chain, Object.class, modifiedBody);
}
// 如果是表单请求
else if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType)){
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
recordLog(exchange.getRequest(), body);
return Mono.just(body);
});
return getVoidMono(exchange, chain, String.class, modifiedBody);
}
recordLog(exchange.getRequest(), "");
return chain.filter(exchange.mutate().request(exchange.getRequest()).build());
}
/**
* 优先级默认设置为最高
* @return
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
private Mono<Void> getVoidMono(ServerWebExchange exchange, GatewayFilterChain chain, Class outClass, Mono<?> modifiedBody) {
BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, outClass);
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
headers.remove(HttpHeaders.CONTENT_LENGTH);
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
return bodyInserter.insert(outputMessage, new BodyInserterContext()).then(Mono.defer(() -> {
ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return outputMessage.getBody();
}
};
return chain.filter(exchange.mutate().request(decorator).build());
}));
}
private void recordLog(ServerHttpRequest request, Object body) {
StringBuilder builder = new StringBuilder(" request url: ");
builder.append(request.getURI().getRawPath());
HttpMethod method = request.getMethod();
if (null != method){
builder.append(", method: ").append(method.name());
}
builder.append(", header { ");
for (Map.Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
builder.append(entry.getKey()).append(":").append(StringUtils.join(entry.getValue(), ",")).append(",");
}
builder.append("} param: ");
if (null != method && HttpMethod.GET.matches(method.name())) {
MultiValueMap<String, String> queryParams = request.getQueryParams();
for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ",")).append(",");
}
}
else {
builder.append(body);
}
writeAccessLog(builder.toString());
}
private void writeAccessLog(String str){
File file = new File("access.log");
if (!file.exists()){
try {
if (file.createNewFile()){
file.setWritable(true);
}
} catch (IOException e) {
log.error("创建访问日志文件失败.{}",e.getMessage(),e);
}
}
try(FileWriter fileWriter = new FileWriter(file.getName(),true)){
fileWriter.write(str);
} catch (IOException e) {
log.error("写访问日志到文件失败. {}", e.getMessage(),e);
}
}
}
最后于 2月前
被admin编辑
,原因: 修复DELETE无法请求
最新回复 (0)
全部楼主