Spring boot 如何使用外部拦截器/解码器以自定义格式记录请求-响应?
我正在为springboot开发一个定制的日志框架,用于记录rest模板请求和响应,目前运行良好。我正试图为“外国客户”实现同样的功能,我面临着两个问题Spring boot 如何使用外部拦截器/解码器以自定义格式记录请求-响应?,spring-boot,spring-cloud-feign,feign,Spring Boot,Spring Cloud Feign,Feign,我正在为springboot开发一个定制的日志框架,用于记录rest模板请求和响应,目前运行良好。我正试图为“外国客户”实现同样的功能,我面临着两个问题 对于请求日志记录,我利用了假请求拦截器,它工作正常,唯一的问题是我无法检索完整的请求URL。 下面的方法只提供了相对URL requestTemplate.url() 要记录响应,我唯一能找到的方法是ResponseDecoder。在那里,我可以检索除有效载荷以外的所有内容。从访问有效负载时 InputStream is = respons
假请求拦截器
,它工作正常,唯一的问题是我无法检索完整的请求URL。
下面的方法只提供了相对URL
requestTemplate.url()
ResponseDecoder
。在那里,我可以检索除有效载荷以外的所有内容。从访问有效负载时
InputStream is = response.body().asInputStream();
String payload = new String(IOUtils.toByteArray(is));
我想知道是否有更好的方法可以像SpringREST模板一样,在Feign中记录请求响应。或者,如果我所采用的方法没有问题,请帮助我解决上述问题。您可以配置一个自定义
feign.Logger
实例来处理此问题。有两个内置的,JavaLogger
使用java.util.logging
和Slf4JLogger
使用slf4j
。您可以通过扩展feign.logger
并将其注册为@Bean
来创建自己的记录器实现
该记录器应在Spring之前获取,并在您的佯装客户端注册。下面是让您开始学习的记录器
基类:
受保护的抽象无效日志(字符串配置键、字符串格式、对象…参数);
创建自己的实例,实现这个方法,然后在请求之前和响应返回之后调用它。无需更新拦截器或创建响应解码器。在RestConfiguration中,您需要升级默认级别的logging FaignClient,并通过@Bean FaignLogger进行覆盖,如:
@Configuration(proxyBeanMethods = false)
@EnableCircuitBreaker
@EnableFeignClients(basePackageClasses = [Application::class])
class RestConfiguration: WebMvcConfigurer {
@Bean
fun feignLoggerLevel(): Logger.Level {
return Logger.Level.FULL
}
@Bean
fun feignLogger(): Logger {
return FeignClientLogger()
}
}
并实施记录器(日志格式):
导入外部记录器
进口外国请求
进口国外响应
导入feign.Util*
导入org.slf4j.LoggerFactory
类faignClientLogger:Logger(){
private val log=LoggerFactory.getLogger(this::class.java)
重写日志请求(configKey:String?,日志级别:Level?,请求:request?){
if(请求==null)
返回
val feignRequest=feignRequest()
feignRequest.method=request.httpMethod().name
feignRequest.url=request.url()
for(request.headers()键中的字段){
for(值中的值或空(request.headers(),字段)){
假装请求.addHeader(字段,值)
}
}
if(request.requestBody()!=null){
feignRequest.body=request.requestBody().asString()
}
log.trace(假装请求.toString())
}
覆盖有趣的日志和缓冲响应(
configKey:字符串?,
日志级别:级别?,
答复:答复?,
时间:长
):回应{
如果(响应==null)
返回响应
val feignResponse=feignResponse()
val status=响应。状态()
faignresponse.status=response.status()
假装回答、理由=
(如果(response.reason()!=null&&logLevel!!>Level.NONE)“+response.reason()else”“)
faignresponse.duration=elapsedTime
if(logLevel!!.ordinal>=Level.HEADERS.ordinal){
for(response.headers().keys中的字段){
for(valuesOrEmpty(response.headers(),field)中的值){
FaignResponse.addHeader(字段,值)
}
}
if(response.body()!=null&&!(status==204 | | status==205)){
val bodyData:ByteArray=toByteArray(response.body().asInputStream())
if(logLevel.ordinal>=Level.FULL.ordinal&&bodyData.isNotEmpty()){
feignResponse.body=decodeodefault(bodyData,UTF_8,“二进制数据”)
}
log.trace(feignResponse.toString())
返回response.toBuilder().body(bodyData.build())
}否则{
log.trace(feignResponse.toString())
}
}
返回响应
}
重写有趣的日志(p0:String?,p1:String?,vararg p2:Any?{}
}
类假反应{
var状态=0
变量原因:字符串?=null
变量持续时间:长=0
私有val头:MutableList=mutableListOf()
变量正文:字符串?=null
fun addHeader(键:String?,值:String?){
headers.add(“$key:$value”)
}
重写funtostring()=
“{”type:“response”,“status:“$status”,“duration:“$duration”,“headers:$headers”,“body:$body”,“reason:$reason”}”
}
类假请求{
变量方法:字符串?=null
变量url:字符串?=null
私有val头:MutableList=mutableListOf()
变量正文:字符串?=null
fun addHeader(键:String?,值:String?){
headers.add(“$key:$value”)
}
重写funtostring()=
“{”类型“:”请求“,”方法“:”方法“,”url“:”url“,”标题“:”标题“,”正文“:$body}”
}
import feign.Logger
import feign.Request
import feign.Response
import feign.Util.*
import org.slf4j.LoggerFactory
class FeignClientLogger : Logger() {
private val log = LoggerFactory.getLogger(this::class.java)
override fun logRequest(configKey: String?, logLevel: Level?, request: Request?) {
if (request == null)
return
val feignRequest = FeignRequest()
feignRequest.method = request.httpMethod().name
feignRequest.url = request.url()
for (field in request.headers().keys) {
for (value in valuesOrEmpty(request.headers(), field)) {
feignRequest.addHeader(field, value)
}
}
if (request.requestBody() != null) {
feignRequest.body = request.requestBody().asString()
}
log.trace(feignRequest.toString())
}
override fun logAndRebufferResponse(
configKey: String?,
logLevel: Level?,
response: Response?,
elapsedTime: Long
): Response? {
if (response == null)
return response
val feignResponse = FeignResponse()
val status = response.status()
feignResponse.status = response.status()
feignResponse.reason =
(if (response.reason() != null && logLevel!! > Level.NONE) " " + response.reason() else "")
feignResponse.duration = elapsedTime
if (logLevel!!.ordinal >= Level.HEADERS.ordinal) {
for (field in response.headers().keys) {
for (value in valuesOrEmpty(response.headers(), field)) {
feignResponse.addHeader(field, value)
}
}
if (response.body() != null && !(status == 204 || status == 205)) {
val bodyData: ByteArray = toByteArray(response.body().asInputStream())
if (logLevel.ordinal >= Level.FULL.ordinal && bodyData.isNotEmpty()) {
feignResponse.body = decodeOrDefault(bodyData, UTF_8, "Binary data")
}
log.trace(feignResponse.toString())
return response.toBuilder().body(bodyData).build()
} else {
log.trace(feignResponse.toString())
}
}
return response
}
override fun log(p0: String?, p1: String?, vararg p2: Any?) {}
}
class FeignResponse {
var status = 0
var reason: String? = null
var duration: Long = 0
private val headers: MutableList<String> = mutableListOf()
var body: String? = null
fun addHeader(key: String?, value: String?) {
headers.add("$key: $value")
}
override fun toString() =
"""{"type":"response","status":"$status","duration":"$duration","headers":$headers,"body":$body,"reason":"$reason"}"""
}
class FeignRequest {
var method: String? = null
var url: String? = null
private val headers: MutableList<String> = mutableListOf()
var body: String? = null
fun addHeader(key: String?, value: String?) {
headers.add("$key: $value")
}
override fun toString() =
"""{"type":"request","method":"$method","url":"$url","headers":$headers,"body":$body}"""
}