From 6275cab4ebb30502f96d5a2c1d523a661660489f Mon Sep 17 00:00:00 2001 From: zhangli <123879394@qq.com> Date: Sat, 12 Jul 2025 22:28:33 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0sczx=5Fcar=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=9A=84=E8=B7=AF=E7=94=B1=E6=98=A0=E5=B0=84=EF=BC=8C=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=AF=B7=E6=B1=82=E5=93=8D=E5=BA=94=E7=9A=84=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 ++ .../component/DynamicRouteScheduler.java | 3 + .../gateway/config/CustomRouteConfig.java | 3 + .../gateway/filter/LoggingGlobalFilter.java | 2 +- .../filter/RequestLogGlobalFilter.java | 96 +++++++++++++++++ .../filter/ResponseLogGlobalFilter.java | 101 ++++++++++++++++++ 6 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/sczx/gateway/filter/RequestLogGlobalFilter.java create mode 100644 src/main/java/com/sczx/gateway/filter/ResponseLogGlobalFilter.java diff --git a/pom.xml b/pom.xml index 3af5481..63cac39 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,13 @@ 1.18.30 + + + com.alibaba + fastjson + 1.2.83 + + diff --git a/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java b/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java index 3889dca..673ebad 100644 --- a/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java +++ b/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java @@ -1,11 +1,13 @@ package com.sczx.gateway.component; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +@Slf4j @Component public class DynamicRouteScheduler { @@ -15,6 +17,7 @@ public class DynamicRouteScheduler { // 每30秒刷新一次路由(可按需调整) @Scheduled(fixedRate = 30_000) public void refreshRoutes() { + log.info("refreshRoutes"); publisher.publishEvent(new RefreshRoutesEvent(this)); } } diff --git a/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java b/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java index 504db30..f847ea9 100644 --- a/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java +++ b/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java @@ -30,6 +30,9 @@ public class CustomRouteConfig { .route("sczx_store", r -> r.path("/zc/store/**") .filters(f -> f.rewritePath("/zc/store/(?.*)", "/${segment}")) .uri(uriWithCustomLoadBalance("sczx_store"))) + .route("sczx_car", r -> r.path("/zc/car/**") + .filters(f -> f.rewritePath("/zc/car/(?.*)", "/${segment}")) + .uri(uriWithCustomLoadBalance("sczx_car"))) .build(); // .route("sczx_user", r -> r.path("/zc/user/**") // .uri("lb://sczx_user")) diff --git a/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java index c028f1e..2a6e9ee 100644 --- a/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java +++ b/src/main/java/com/sczx/gateway/filter/LoggingGlobalFilter.java @@ -25,7 +25,7 @@ import java.nio.charset.StandardCharsets; * 适用于 Spring Boot 2.3.x + Spring Cloud Hoxton.SR12 环境。 */ @Slf4j -@Component +//@Component public class LoggingGlobalFilter implements GlobalFilter, Ordered { @Override diff --git a/src/main/java/com/sczx/gateway/filter/RequestLogGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/RequestLogGlobalFilter.java new file mode 100644 index 0000000..34efd69 --- /dev/null +++ b/src/main/java/com/sczx/gateway/filter/RequestLogGlobalFilter.java @@ -0,0 +1,96 @@ +package com.sczx.gateway.filter; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.support.ServerWebExchangeUtils; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Configuration +@Slf4j +public class RequestLogGlobalFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + URI uri = request.getURI(); + //String path = request.getPath().value(); + String path = request.getPath().pathWithinApplication().value();//打印请求路径 + String requestUrl = this.getOriginalRequestUrl(exchange);//打印请求url + String method = request.getMethodValue(); + //cors + HttpHeaders headers = request.getHeaders(); + + log.info("--> method: {} url: {} header: {}", method, requestUrl, headers); + if ("POST".equals(method)) { + return DataBufferUtils.join(exchange.getRequest().getBody()) + .flatMap(dataBuffer -> { + byte[] bytes = new byte[dataBuffer.readableByteCount()]; + dataBuffer.read(bytes); + String bodyString = new String(bytes, StandardCharsets.UTF_8); + log.info("--> {}", bodyString); + //log.info("--> {}", formatStr(bodyString)); //formData + exchange.getAttributes().put("POST_BODY", bodyString); + DataBufferUtils.release(dataBuffer); + Flux cachedFlux = Flux.defer(() -> { + DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); + return Mono.just(buffer); + }); + + ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) { + @Override + public Flux getBody() { return cachedFlux; } + }; + + return chain.filter(exchange.mutate().request(mutatedRequest).build()); + }); + } else if ("GET".equals(method)) { + MultiValueMap queryParams = request.getQueryParams(); + log.info("请求参数:" + queryParams); + return chain.filter(exchange); + } + return chain.filter(exchange); + } + private String getOriginalRequestUrl(ServerWebExchange exchange) { + ServerHttpRequest req = exchange.getRequest(); + LinkedHashSet uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + URI requestUri = uris.stream().findFirst().orElse(req.getURI()); + MultiValueMap queryParams = req.getQueryParams(); + //打印 /api/rest/feign/order/detail + // return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString(); + + return requestUri.toString(); // http://localhost:8091/api/rest/feign/order/detail + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + /** + * 去掉FormData 空格,换行和制表符 + */ + private static String formatStr(String str){ + if (str != null && str.length() > 0) { + Pattern p = Pattern.compile("\\s*|\t|\r|\n"); + Matcher m = p.matcher(str); + return m.replaceAll(""); + } + return str; + } +} diff --git a/src/main/java/com/sczx/gateway/filter/ResponseLogGlobalFilter.java b/src/main/java/com/sczx/gateway/filter/ResponseLogGlobalFilter.java new file mode 100644 index 0000000..a6fb91e --- /dev/null +++ b/src/main/java/com/sczx/gateway/filter/ResponseLogGlobalFilter.java @@ -0,0 +1,101 @@ +package com.sczx.gateway.filter; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.Feature; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.serializer.ValueFilter; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.core.io.buffer.DefaultDataBufferFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.http.server.reactive.ServerHttpResponseDecorator; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Configuration +public class ResponseLogGlobalFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + try { + ServerHttpResponse originalResponse = exchange.getResponse(); + DataBufferFactory bufferFactory = originalResponse.bufferFactory(); + HttpStatus statusCode = originalResponse.getStatusCode(); + if (statusCode != HttpStatus.OK) { + return chain.filter(exchange);//降级处理返回数据 + } + ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) { + @Override + public Mono writeWith(Publisher body) { + if (body instanceof Flux) { + Flux fluxBody = Flux.from(body); + + return super.writeWith(fluxBody.buffer().map(dataBuffers -> { + + // 合并多个流集合,解决返回体分段传输 + DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(); + DataBuffer buff = dataBufferFactory.join(dataBuffers); + byte[] content = new byte[buff.readableByteCount()]; + buff.read(content); + DataBufferUtils.release(buff);//释放掉内存 + + //排除Excel导出,不是application/json不打印。若请求是上传图片则在最上面判断。 + MediaType contentType = originalResponse.getHeaders().getContentType(); + if (!MediaType.APPLICATION_JSON.isCompatibleWith(contentType)) { + return bufferFactory.wrap(content); + } + + // 构建返回日志 + String joinData = new String(content); + String result = modifyBody(joinData); + List rspArgs = new ArrayList<>(); + rspArgs.add(originalResponse.getStatusCode().value()); + rspArgs.add(exchange.getRequest().getURI()); + rspArgs.add(result); + log.info("<-- {} {}\n{}", rspArgs.toArray()); + + getDelegate().getHeaders().setContentLength(result.getBytes().length); + return bufferFactory.wrap(result.getBytes()); + })); + } else { + log.error("<-- {} 响应code异常", getStatusCode()); + } + return super.writeWith(body); + } + }; + return chain.filter(exchange.mutate().response(decoratedResponse).build()); + + } catch (Exception e) { + log.error("gateway log exception.\n" + e); + return chain.filter(exchange); + } + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + + //返回统一的JSON日期数据 2024-02-23 11:00, null转空字符串 + private String modifyBody(String jsonStr){ + JSONObject json = JSON.parseObject(jsonStr, Feature.AllowISO8601DateFormat); + JSONObject.DEFFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm"; + return JSONObject.toJSONString(json, (ValueFilter) (object, name, value) -> value == null ? "" : value, SerializerFeature.WriteDateUseDateFormat); + } +} +