如何在Spring WebFlux中记录请求和响应正文
我想在带有Kotlin的Spring WebFlux上的REST API中集中记录请求和响应.到目前为止,我已经尝试过这种方法
I want to have centralised logging for requests and responses in my REST API on Spring WebFlux with Kotlin. So far I've tried this approaches
@Bean
fun apiRouter() = router {
(accept(MediaType.APPLICATION_JSON) and "/api").nest {
"/user".nest {
GET("/", userHandler::listUsers)
POST("/{userId}", userHandler::updateUser)
}
}
}.filter { request, next ->
logger.info { "Processing request $request with body ${request.bodyToMono<String>()}" }
next.handle(request).doOnSuccess { logger.info { "Handling with response $it" } }
}
这里请求方法和路径已成功记录,但正文为Mono
,那么我应该如何记录呢?是否应该相反,我必须订阅请求正文Mono
并将其记录在回调中?
另一个问题是,这里的ServerResponse
接口无法访问响应主体.我怎么在这里买?
Here request method and path log successfully but the body is Mono
, so how should I log it? Should it be the other way around and I have to subscribe on request body Mono
and log it in the callback?
Another problem is that ServerResponse
interface here doesn't have access to the response body. How can I get it here?
我尝试过的另一种方法是使用WebFilter
Another approach I've tried is using WebFilter
@Bean
fun loggingFilter(): WebFilter =
WebFilter { exchange, chain ->
val request = exchange.request
logger.info { "Processing request method=${request.method} path=${request.path.pathWithinApplication()} params=[${request.queryParams}] body=[${request.body}]" }
val result = chain.filter(exchange)
logger.info { "Handling with response ${exchange.response}" }
return@WebFilter result
}
这里有同样的问题:请求正文是Flux
,没有响应正文.
Same problem here: request body is Flux
and no response body.
是否可以通过某些过滤器访问完整的请求和响应以进行日志记录?我不明白吗?
Is there a way to access full request and response for logging from some filters? What don't I understand?
这与Spring MVC中的情况或多或少相似.
This is more or less similar to the situation in Spring MVC.
在Spring MVC中,可以使用AbstractRequestLoggingFilter
过滤器和ContentCachingRequestWrapper
和/或ContentCachingResponseWrapper
.这里有很多折衷:
In Spring MVC, you can use a AbstractRequestLoggingFilter
filter and ContentCachingRequestWrapper
and/or ContentCachingResponseWrapper
. Many tradeoffs here:
- 如果您想访问servlet请求属性,则需要实际读取并解析请求正文
- 记录请求正文意味着缓冲请求正文,这可能会占用大量内存
- 如果要访问响应正文,则需要包装响应并在编写响应正文时对其进行缓冲,以供以后检索
ContentCaching*Wrapper
类在WebFlux中不存在,但是您可以创建类似的类.但是请记住这里的其他要点:
ContentCaching*Wrapper
classes don't exist in WebFlux but you could create similar ones. But keep in mind other points here:
- 在内存中缓冲数据以某种方式违反了反应堆,因为我们正在尝试使用可用资源来提高效率
- 您不应该篡改实际的数据流,并且不应该频繁/频繁地刷新数据,否则就有可能破坏流式传输用例的风险 在该级别的
- 中,您只能访问
DataBuffer
实例,这些实例(大约)是内存有效的字节数组.这些属于缓冲池,并被回收以用于其他交换.如果未正确保留/释放这些内存,则会造成内存泄漏(并且缓冲数据以供日后使用肯定适合这种情况)
再次在该级别上,它只有字节,您无权访问任何编解码器来解析HTTP正文.如果最初不是人类可读的内容,我会忘记缓冲内容
- buffering data in memory somehow goes against the reactive stack, since we're trying there to be very efficient with the available resources
- you should not tamper with the actual flow of data and flush more/less often than expected, otherwise you'd risk breaking streaming uses cases
- at that level, you only have access to
DataBuffer
instances, which are (roughly) memory-efficient byte arrays. Those belong to buffer pools and are recycled for other exchanges. If those aren't properly retained/released, memory leaks are created (and buffering data for later consumption certainly fits that scenario) - again at that level, it's only bytes and you don't have access to any codec to parse the HTTP body. I'd forget about buffering the content if it's not human-readable in the first place
您问题的其他答案:
- 是的,
WebFilter
可能是最好的方法 - 不,您不应该订阅请求正文,否则您将消耗处理程序将无法读取的数据;您可以在请求上
flatMap
并在doOn
运算符中缓冲数据 - 包装响应应该使您可以访问正在编写的响应正文;不过不要忘记内存泄漏
- yes, the
WebFilter
is probably the best approach - no, you shouldn't subscribe to the request body otherwise you'd consume data that the handler won't be able to read; you can
flatMap
on the request and buffer data indoOn
operators - wrapping the response should give you access to the response body as it's being written; don't forget about memory leaks, though