-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: add api log && global exception for gateway
- Loading branch information
Showing
7 changed files
with
351 additions
and
0 deletions.
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
...rtframework/cloud/examples/support/gateway/configure/GatewayErrorWebExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package org.smartframework.cloud.examples.support.gateway.configure; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.smartframework.cloud.common.pojo.Base; | ||
import org.smartframework.cloud.common.pojo.vo.RespVO; | ||
import org.smartframework.cloud.examples.support.gateway.filter.log.ApiLogDO; | ||
import org.smartframework.cloud.examples.support.gateway.filter.log.LogUtil; | ||
import org.smartframework.cloud.starter.web.exception.ExceptionHandlerContext; | ||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.server.reactive.ServerHttpResponse; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import reactor.core.publisher.Mono; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
|
||
/** | ||
* 全局异常处理 | ||
* | ||
* @author liyulin | ||
* @date 2020-07-21 | ||
*/ | ||
@Slf4j | ||
@Configuration | ||
public class GatewayErrorWebExceptionHandler implements ErrorWebExceptionHandler { | ||
|
||
@Override | ||
public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) { | ||
log.error("gateway.errorlog", throwable); | ||
RespVO<Base> respVO = new RespVO<>(ExceptionHandlerContext.transRespHead(throwable)); | ||
String response = respVO.toString(); | ||
printErrorLog(response); | ||
|
||
ServerHttpResponse serverHttpResponse = exchange.getResponse(); | ||
serverHttpResponse.getHeaders().setContentType(MediaType.APPLICATION_JSON_UTF8); | ||
return serverHttpResponse.writeWith(Mono.fromSupplier(() -> { | ||
return serverHttpResponse.bufferFactory().wrap(response.getBytes(StandardCharsets.UTF_8)); | ||
})); | ||
} | ||
|
||
/** | ||
* 错误请求日志打印 | ||
* | ||
* @param response | ||
*/ | ||
private void printErrorLog(String response) { | ||
ApiLogDO apiLogDO = LogUtil.getApiLogCache().get(); | ||
if (apiLogDO != null) { | ||
apiLogDO.setCost(System.currentTimeMillis() - apiLogDO.getCost()); | ||
apiLogDO.setResult(response); | ||
log.info("gateway.log.error=>{}", apiLogDO); | ||
} | ||
} | ||
|
||
} |
18 changes: 18 additions & 0 deletions
18
...eway/src/main/java/org/smartframework/cloud/examples/support/gateway/constants/Order.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package org.smartframework.cloud.examples.support.gateway.constants; | ||
|
||
import org.springframework.core.Ordered; | ||
|
||
/** | ||
* order | ||
* | ||
* @author liyulin | ||
* @date 2020-07-17 | ||
*/ | ||
public interface Order { | ||
|
||
/** | ||
* 请求日志order | ||
*/ | ||
int REQUEST_LOG = Ordered.HIGHEST_PRECEDENCE; | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
.../src/main/java/org/smartframework/cloud/examples/support/gateway/filter/log/ApiLogDO.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.smartframework.cloud.examples.support.gateway.filter.log; | ||
|
||
import com.fasterxml.jackson.annotation.JsonInclude; | ||
import lombok.*; | ||
|
||
import java.io.Serializable; | ||
|
||
/** | ||
* @author liyulin | ||
* @date 2020-07-17 | ||
*/ | ||
@Setter | ||
@Getter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
@ToString | ||
@Builder | ||
@JsonInclude(JsonInclude.Include.NON_NULL) | ||
public class ApiLogDO implements Serializable { | ||
|
||
/** | ||
* 请求路径 | ||
*/ | ||
private String url; | ||
/** | ||
* http请求方式 | ||
*/ | ||
private String method; | ||
/** | ||
* http头部数据 | ||
*/ | ||
private String head; | ||
/** | ||
* url参数 | ||
*/ | ||
private String queryParams; | ||
/** | ||
* body部分请求体参数 | ||
*/ | ||
private Object args; | ||
/** | ||
* 请求结果 | ||
*/ | ||
private String result; | ||
/** | ||
* 花费时间(毫秒) | ||
*/ | ||
private long cost; | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
...n/java/org/smartframework/cloud/examples/support/gateway/filter/log/GatewayLogFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.smartframework.cloud.examples.support.gateway.filter.log; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.smartframework.cloud.examples.support.gateway.constants.Order; | ||
import org.springframework.core.Ordered; | ||
import org.springframework.http.server.reactive.ServerHttpRequest; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import org.springframework.web.server.WebFilter; | ||
import org.springframework.web.server.WebFilterChain; | ||
import reactor.core.publisher.Mono; | ||
import reactor.core.publisher.SignalType; | ||
|
||
/** | ||
* 打印请求、响应日志 | ||
* | ||
* @author liyulin | ||
* @date 2020-07-17 | ||
*/ | ||
@Component | ||
@Slf4j | ||
public class GatewayLogFilter implements WebFilter, Ordered { | ||
|
||
@Override | ||
public int getOrder() { | ||
return Order.REQUEST_LOG; | ||
} | ||
|
||
@Override | ||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { | ||
fillLog(exchange.getRequest()); | ||
|
||
LogServerHttpRequestDecorator requestDecorator = new LogServerHttpRequestDecorator(exchange.getRequest()); | ||
LogServerHttpResponseDecorator responseWrapper = new LogServerHttpResponseDecorator(exchange.getResponse()); | ||
|
||
return chain.filter(exchange.mutate().request(requestDecorator).response(responseWrapper).build()).doFinally(signalType -> { | ||
if (SignalType.ON_ERROR.compareTo(signalType) != 0) { | ||
ApiLogDO apiLogDO = LogUtil.getApiLogCache().get(); | ||
if (apiLogDO != null) { | ||
apiLogDO.setCost(System.currentTimeMillis() - apiLogDO.getCost()); | ||
log.info("gateway.log=>{}", apiLogDO); | ||
} | ||
} | ||
LogUtil.getApiLogCache().remove(); | ||
}); | ||
} | ||
|
||
private void fillLog(ServerHttpRequest request) { | ||
final String path = request.getURI().getPath(); | ||
final String query = request.getURI().getQuery(); | ||
|
||
ApiLogDO apiLogDO = new ApiLogDO(); | ||
// 此处cost存储请求开始时间 | ||
apiLogDO.setCost(System.currentTimeMillis()); | ||
apiLogDO.setMethod(request.getMethod().name()); | ||
apiLogDO.setUrl(path + (StringUtils.isBlank(query) ? "" : "?" + query)); | ||
apiLogDO.setHead(request.getHeaders().toString()); | ||
LogUtil.getApiLogCache().set(apiLogDO); | ||
} | ||
|
||
} |
36 changes: 36 additions & 0 deletions
36
...artframework/cloud/examples/support/gateway/filter/log/LogServerHttpRequestDecorator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package org.smartframework.cloud.examples.support.gateway.filter.log; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.core.io.buffer.DataBuffer; | ||
import org.springframework.http.server.reactive.ServerHttpRequest; | ||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.scheduler.Schedulers; | ||
|
||
/** | ||
* 包装ServerHttpRequest对象,获取请求body参数 | ||
* @author liyulin | ||
* @date 2020-07-21 | ||
*/ | ||
@Slf4j | ||
public class LogServerHttpRequestDecorator extends ServerHttpRequestDecorator { | ||
|
||
private Flux<DataBuffer> body; | ||
|
||
LogServerHttpRequestDecorator(ServerHttpRequest delegate) { | ||
super(delegate); | ||
Flux<DataBuffer> flux = super.getBody(); | ||
if (LogUtil.legalLogMediaTypes.contains(delegate.getHeaders().getContentType())) { | ||
body = flux.publishOn(Schedulers.single()).map(dataBuffer -> | ||
LogUtil.chain(LogUtil.DataType.REQUEST, dataBuffer, LogUtil.getApiLogCache().get())); | ||
} else { | ||
body = flux; | ||
} | ||
} | ||
|
||
@Override | ||
public Flux<DataBuffer> getBody() { | ||
return body; | ||
} | ||
|
||
} |
50 changes: 50 additions & 0 deletions
50
...rtframework/cloud/examples/support/gateway/filter/log/LogServerHttpResponseDecorator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.smartframework.cloud.examples.support.gateway.filter.log; | ||
|
||
import lombok.extern.slf4j.Slf4j; | ||
import org.reactivestreams.Publisher; | ||
import org.springframework.core.io.buffer.DataBuffer; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.server.reactive.ServerHttpResponse; | ||
import org.springframework.http.server.reactive.ServerHttpResponseDecorator; | ||
import reactor.core.publisher.Flux; | ||
import reactor.core.publisher.Mono; | ||
import reactor.core.scheduler.Schedulers; | ||
|
||
/** | ||
* 包装ServerHttpResponse,获取响应结果 | ||
* | ||
* @author liyulin | ||
* @date 2020-07-21 | ||
*/ | ||
@Slf4j | ||
public class LogServerHttpResponseDecorator extends ServerHttpResponseDecorator { | ||
|
||
LogServerHttpResponseDecorator(ServerHttpResponse delegate) { | ||
super(delegate); | ||
} | ||
|
||
@Override | ||
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { | ||
return super.writeAndFlushWith(body); | ||
} | ||
|
||
@Override | ||
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { | ||
final MediaType contentType = super.getHeaders().getContentType(); | ||
if (LogUtil.legalLogMediaTypes.contains(contentType)) { | ||
if (body instanceof Mono) { | ||
final Mono<DataBuffer> monoBody = (Mono<DataBuffer>) body; | ||
return super.writeWith(monoBody.publishOn(Schedulers.single()) | ||
.map(dataBuffer | ||
-> LogUtil.chain(LogUtil.DataType.RESPONSE, dataBuffer, LogUtil.getApiLogCache().get()))); | ||
} else if (body instanceof Flux) { | ||
final Flux<DataBuffer> monoBody = (Flux<DataBuffer>) body; | ||
return super.writeWith(monoBody.publishOn(Schedulers.single()) | ||
.map(dataBuffer | ||
-> LogUtil.chain(LogUtil.DataType.RESPONSE, dataBuffer, LogUtil.getApiLogCache().get()))); | ||
} | ||
} | ||
return super.writeWith(body); | ||
} | ||
|
||
} |
79 changes: 79 additions & 0 deletions
79
...y/src/main/java/org/smartframework/cloud/examples/support/gateway/filter/log/LogUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package org.smartframework.cloud.examples.support.gateway.filter.log; | ||
|
||
import com.google.common.collect.Lists; | ||
import io.netty.buffer.UnpooledByteBufAllocator; | ||
import lombok.Getter; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.apache.commons.io.IOUtils; | ||
import org.springframework.core.io.buffer.DataBuffer; | ||
import org.springframework.core.io.buffer.DataBufferUtils; | ||
import org.springframework.core.io.buffer.NettyDataBufferFactory; | ||
import org.springframework.http.MediaType; | ||
|
||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.List; | ||
|
||
/** | ||
* @author liyulin | ||
* @date 2020-07-21 | ||
*/ | ||
@Slf4j | ||
public class LogUtil { | ||
|
||
/** | ||
* 打印日志的http content-type类型 | ||
*/ | ||
public static final List<MediaType> legalLogMediaTypes = Lists.newArrayList(MediaType.APPLICATION_XML, | ||
MediaType.APPLICATION_JSON, | ||
MediaType.APPLICATION_JSON_UTF8, | ||
MediaType.TEXT_PLAIN, | ||
MediaType.TEXT_XML); | ||
|
||
/** | ||
* 存储临时日志 | ||
*/ | ||
@Getter | ||
private static ThreadLocal<ApiLogDO> apiLogCache = new InheritableThreadLocal<>(); | ||
|
||
public static <T extends DataBuffer> T chain(DataType dataType, T buffer, ApiLogDO apiLogDO) { | ||
try { | ||
InputStream dataBuffer = buffer.asInputStream(); | ||
byte[] bytes = IOUtils.toByteArray(dataBuffer); | ||
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false)); | ||
|
||
String data = new String(bytes, StandardCharsets.UTF_8); | ||
// 请求数据 | ||
if (dataType == DataType.REQUEST) { | ||
apiLogDO.setArgs(data); | ||
} | ||
// 响应数据 | ||
else if (data != null) { | ||
// 超过长度的截掉 | ||
apiLogDO.setResult(data.length() <= 1024 ? data : data.substring(0, 1024)); | ||
} | ||
|
||
DataBufferUtils.release(buffer); | ||
return (T) nettyDataBufferFactory.wrap(bytes); | ||
} catch (IOException e) { | ||
log.error(e.getMessage(), e); | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* 数据类型 | ||
*/ | ||
static enum DataType { | ||
/** | ||
* 请求数据 | ||
*/ | ||
REQUEST, | ||
/** | ||
* 响应数据 | ||
*/ | ||
RESPONSE; | ||
} | ||
|
||
} |