增加sczx_car服务的路由映射,增加请求响应的日志

This commit is contained in:
2025-07-12 22:28:33 +08:00
parent 2fa4709f6d
commit 6275cab4eb
6 changed files with 211 additions and 1 deletions

View File

@ -96,6 +96,13 @@
<version> 1.18.30</version> <version> 1.18.30</version>
</dependency> </dependency>
<!-- FastJSON -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies> </dependencies>
<!-- Build Configuration --> <!-- Build Configuration -->

View File

@ -1,11 +1,13 @@
package com.sczx.gateway.component; package com.sczx.gateway.component;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent; import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Slf4j
@Component @Component
public class DynamicRouteScheduler { public class DynamicRouteScheduler {
@ -15,6 +17,7 @@ public class DynamicRouteScheduler {
// 每30秒刷新一次路由可按需调整 // 每30秒刷新一次路由可按需调整
@Scheduled(fixedRate = 30_000) @Scheduled(fixedRate = 30_000)
public void refreshRoutes() { public void refreshRoutes() {
log.info("refreshRoutes");
publisher.publishEvent(new RefreshRoutesEvent(this)); publisher.publishEvent(new RefreshRoutesEvent(this));
} }
} }

View File

@ -30,6 +30,9 @@ public class CustomRouteConfig {
.route("sczx_store", r -> r.path("/zc/store/**") .route("sczx_store", r -> r.path("/zc/store/**")
.filters(f -> f.rewritePath("/zc/store/(?<segment>.*)", "/${segment}")) .filters(f -> f.rewritePath("/zc/store/(?<segment>.*)", "/${segment}"))
.uri(uriWithCustomLoadBalance("sczx_store"))) .uri(uriWithCustomLoadBalance("sczx_store")))
.route("sczx_car", r -> r.path("/zc/car/**")
.filters(f -> f.rewritePath("/zc/car/(?<segment>.*)", "/${segment}"))
.uri(uriWithCustomLoadBalance("sczx_car")))
.build(); .build();
// .route("sczx_user", r -> r.path("/zc/user/**") // .route("sczx_user", r -> r.path("/zc/user/**")
// .uri("lb://sczx_user")) // .uri("lb://sczx_user"))

View File

@ -25,7 +25,7 @@ import java.nio.charset.StandardCharsets;
* 适用于 Spring Boot 2.3.x + Spring Cloud Hoxton.SR12 环境。 * 适用于 Spring Boot 2.3.x + Spring Cloud Hoxton.SR12 环境。
*/ */
@Slf4j @Slf4j
@Component //@Component
public class LoggingGlobalFilter implements GlobalFilter, Ordered { public class LoggingGlobalFilter implements GlobalFilter, Ordered {
@Override @Override

View File

@ -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<Void> 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<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() { return cachedFlux; }
};
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
} else if ("GET".equals(method)) {
MultiValueMap<String, String> queryParams = request.getQueryParams();
log.info("请求参数:" + queryParams);
return chain.filter(exchange);
}
return chain.filter(exchange);
}
private String getOriginalRequestUrl(ServerWebExchange exchange) {
ServerHttpRequest req = exchange.getRequest();
LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
URI requestUri = uris.stream().findFirst().orElse(req.getURI());
MultiValueMap<String, String> 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;
}
}

View File

@ -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<Void> 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<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> 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<Object> 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);
}
}