增加sczx_car服务的路由映射,增加请求响应的日志
This commit is contained in:
7
pom.xml
7
pom.xml
@ -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 -->
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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"))
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user