Spring Cloud Gateway记录日志

admin 2月前 696

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)
全部楼主
返回
发新帖