mirror of
https://github.moeyy.xyz/https://github.com/GeWuYou/forgeboot
synced 2025-10-27 16:14:32 +08:00
feat(trace): Refactoring the tracking module - Remove the ignorePatten property in TraceProperties
- Delete FeignTraceAutoConfiguration, WebClientTraceAutoConfiguration, and RequestIdTaskDecorator - Refactor TraceAutoConfiguration and use ContextFieldContributor to replace the original filters and interceptors - Remove the RequestIdUtil class and use StringContextHolder to replace thread local storage - Update TraceRequestIdProvider and use ContextFieldContributor to provide the request ID - Remove useless extension functions and filter classes
This commit is contained in:
parent
b90048a57d
commit
0e74483168
@ -21,10 +21,4 @@ class TraceProperties {
|
||||
* MDC(Mapped Diagnostic Context)中用于存储请求ID的键名,默认为"requestId"。
|
||||
*/
|
||||
var requestIdMdcKey: String = "requestId"
|
||||
|
||||
/**
|
||||
* 配置忽略日志记录的路径模式,通常用于静态资源文件,
|
||||
* 默认忽略以.css、.js、.png等结尾的静态资源请求。
|
||||
*/
|
||||
var ignorePatten = arrayOf(".*\\.(css|js|png|jpg|jpeg|gif|svg)")
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.plugin.spring)
|
||||
}
|
||||
dependencies {
|
||||
implementation(project(Modules.Core.EXTENSION))
|
||||
compileOnly(platform(libs.springBootDependencies.bom))
|
||||
implementation(platform(libs.springBootDependencies.bom))
|
||||
implementation(libs.springBoot.autoconfigure)
|
||||
compileOnly(platform(libs.springCloudDependencies.bom))
|
||||
compileOnly(libs.springBootStarter.web)
|
||||
compileOnly(libs.springBootStarter.webflux)
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.autoconfig
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
*Feign跟踪自动配置
|
||||
*
|
||||
* @since 2025-05-31 22:02:56
|
||||
* @author gewuyou
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = ["feign.RequestInterceptor"])
|
||||
open class FeignTraceAutoConfiguration {
|
||||
/**
|
||||
* Feign 拦截器(仅当 Feign 存在时生效)
|
||||
*
|
||||
* 该拦截器用于在Feign客户端中传递请求ID
|
||||
* @param traceProperties 跟踪配置属性
|
||||
* @return FeignRequestIdInterceptor实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = ["feignRequestIdInterceptor"])
|
||||
open fun feignRequestIdInterceptor(traceProperties: TraceProperties): Any {
|
||||
val clazz = Class.forName("com.gewuyou.forgeboot.trace.impl.interceptor.FeignRequestIdInterceptor")
|
||||
val constructor = clazz.getConstructor(TraceProperties::class.java)
|
||||
log.info( "创建FeignRequestIdInterceptor实例")
|
||||
return constructor.newInstance(traceProperties)
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,18 @@
|
||||
package com.gewuyou.forgeboot.trace.autoconfig
|
||||
|
||||
|
||||
import com.gewuyou.forgeboot.context.api.ContextFieldContributor
|
||||
import com.gewuyou.forgeboot.context.api.entities.FieldDef
|
||||
import com.gewuyou.forgeboot.context.api.enums.Scope
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.RequestIdProvider
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.decorator.RequestIdTaskDecorator
|
||||
import com.gewuyou.forgeboot.trace.impl.filter.ReactiveRequestIdFilter
|
||||
import com.gewuyou.forgeboot.trace.impl.filter.RequestIdFilter
|
||||
import com.gewuyou.forgeboot.trace.impl.provider.TraceRequestIdProvider
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* 跟踪自动配置
|
||||
@ -23,37 +23,9 @@ import org.springframework.context.annotation.Configuration
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TraceProperties::class)
|
||||
open class TraceAutoConfiguration {
|
||||
/**
|
||||
* Spring MVC 过滤器(仅当 Spring MVC 存在时生效)
|
||||
*
|
||||
* 该过滤器用于在Spring MVC应用中生成和传递请求ID
|
||||
* @param traceProperties 跟踪配置属性
|
||||
* @return RequestIdFilter实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "servlet", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean
|
||||
open fun requestIdFilter(traceProperties: TraceProperties): RequestIdFilter {
|
||||
log.info("RequestIdFilter 已创建!")
|
||||
return RequestIdFilter(traceProperties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Spring WebFlux 过滤器(仅当 Spring WebFlux 存在时生效)
|
||||
*
|
||||
* 该过滤器用于在Spring WebFlux应用中生成和传递请求ID
|
||||
* @param traceProperties 跟踪配置属性
|
||||
* @return ReactiveRequestIdFilter实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = ["spring.main.web-application-type"], havingValue = "reactive")
|
||||
@ConditionalOnMissingBean
|
||||
open fun reactiveRequestIdFilter(traceProperties: TraceProperties): ReactiveRequestIdFilter {
|
||||
log.info("ReactiveRequestIdFilter 已创建!")
|
||||
return ReactiveRequestIdFilter(traceProperties)
|
||||
}
|
||||
|
||||
class TraceAutoConfiguration(
|
||||
private val traceProperties: TraceProperties
|
||||
) {
|
||||
/**
|
||||
* 请求ID提供者(用于生成请求ID)
|
||||
*
|
||||
@ -62,23 +34,19 @@ open class TraceAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(RequestIdProvider::class)
|
||||
open fun traceRequestIdProvider(): TraceRequestIdProvider {
|
||||
fun traceRequestIdProvider(): TraceRequestIdProvider {
|
||||
log.info("TraceRequestIdProvider 已创建!")
|
||||
return TraceRequestIdProvider()
|
||||
return TraceRequestIdProvider(traceProperties)
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程池装饰器(用于 @Async)
|
||||
*
|
||||
* 该装饰器用于在异步线程池中传递请求ID,确保异步执行的任务能够携带正确地请求信息
|
||||
* @param traceProperties 跟踪配置属性
|
||||
* @return RequestIdTaskDecorator实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
open fun requestIdTaskDecorator(traceProperties: TraceProperties): RequestIdTaskDecorator {
|
||||
log.info("RequestIdTaskDecorator 已创建!")
|
||||
return RequestIdTaskDecorator(traceProperties)
|
||||
fun requestContributor() = ContextFieldContributor {
|
||||
setOf(
|
||||
FieldDef(
|
||||
header = traceProperties.requestIdHeaderName, // 请求-响应头名
|
||||
key = traceProperties.requestIdMdcKey, // ctx/MDC 键
|
||||
generator = { UUID.randomUUID().toString() }, // 如果前端没带,用这个生成
|
||||
scopes = setOf(Scope.HEADER, Scope.MDC, Scope.REACTOR)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.autoconfig
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.filter.WebClientRequestIdFilter
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
/**
|
||||
* Web客户端跟踪自动配置
|
||||
*
|
||||
* 该配置类用于自动配置Web客户端的请求ID过滤器,以实现请求跟踪功能
|
||||
* 它依赖于特定条件,如类的存在和Bean的定义,以确保在适当的时候进行配置
|
||||
*
|
||||
* @since 2025-05-31 21:59:02
|
||||
* @author gewuyou
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(name = ["org.springframework.web.reactive.function.client.WebClient\$Builder"])
|
||||
open class WebClientTraceAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 配置Web客户端的请求ID过滤器
|
||||
*
|
||||
* 该方法在满足条件时被调用,它获取WebClient.Builder实例并应用请求ID过滤器,
|
||||
* 以便在发出请求时添加请求ID信息这对于跟踪请求跨多个服务非常有用
|
||||
*
|
||||
* @param webClientBuilder Web客户端构建器,用于配置过滤器
|
||||
* @param traceProperties 跟踪属性配置,用于定制跟踪行为
|
||||
* @return 配置后的WebClient.Builder实例
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(name = ["webClientBuilder"])
|
||||
open fun webClientRequestIdFilter(
|
||||
webClientBuilder: org.springframework.web.reactive.function.client.WebClient.Builder,
|
||||
traceProperties: TraceProperties
|
||||
): org.springframework.web.reactive.function.client.WebClient.Builder {
|
||||
log .info("配置Web客户端的请求ID过滤器")
|
||||
return webClientBuilder.filter(WebClientRequestIdFilter(traceProperties))
|
||||
}
|
||||
}
|
||||
@ -1,3 +1 @@
|
||||
com.gewuyou.forgeboot.trace.autoconfig.TraceAutoConfiguration
|
||||
com.gewuyou.forgeboot.trace.autoconfig.WebClientTraceAutoConfiguration
|
||||
com.gewuyou.forgeboot.trace.autoconfig.FeignTraceAutoConfiguration
|
||||
com.gewuyou.forgeboot.trace.autoconfig.TraceAutoConfiguration
|
||||
@ -1,7 +1,8 @@
|
||||
dependencies {
|
||||
implementation(project(Modules.Core.EXTENSION))
|
||||
implementation(platform(libs.springBootDependencies.bom))
|
||||
api(project(Modules.Core.EXTENSION))
|
||||
api(project(Modules.Context.STARTER))
|
||||
compileOnly(project(Modules.TRACE.API))
|
||||
compileOnly(platform(libs.springBootDependencies.bom))
|
||||
compileOnly(platform(libs.springCloudDependencies.bom))
|
||||
compileOnly(libs.springBootStarter.webflux)
|
||||
compileOnly(libs.springBootStarter.web)
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.decorator
|
||||
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.core.task.TaskDecorator
|
||||
|
||||
/**
|
||||
*请求ID任务装饰器
|
||||
*
|
||||
* @since 2025-03-17 16:57:54
|
||||
* @author gewuyou
|
||||
*/
|
||||
class RequestIdTaskDecorator(
|
||||
private val traceProperties: TraceProperties
|
||||
) : TaskDecorator {
|
||||
override fun decorate(task: Runnable): Runnable {
|
||||
val requestIdMdcKey = traceProperties.requestIdMdcKey
|
||||
// 获取主线程 requestId
|
||||
val requestId = MDC.get(requestIdMdcKey)
|
||||
return Runnable {
|
||||
try {
|
||||
MDC.put(requestIdMdcKey, requestId)
|
||||
task.run()
|
||||
} finally {
|
||||
MDC.remove(requestIdMdcKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.extension
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import org.springframework.http.HttpMethod
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest
|
||||
import org.springframework.web.reactive.function.client.ClientRequest
|
||||
|
||||
/**
|
||||
* 请求扩展
|
||||
*
|
||||
* @since 2025-05-02 21:59:26
|
||||
* @author gewuyou
|
||||
*/
|
||||
|
||||
/**
|
||||
* 判断是否跳过请求
|
||||
* @apiNote 这个方法是给反应式请求id过滤器使用的
|
||||
* @return true: 跳过请求; false: 不跳过请求
|
||||
*/
|
||||
fun ServerHttpRequest.isSkipRequest(ignorePaths: Array<String>): Boolean {
|
||||
return isSkipRequest(this.method.name(), this.uri.path, ignorePaths)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否跳过请求
|
||||
* @apiNote 这个方法是给请求id过滤器使用的
|
||||
* @return true: 跳过请求; false: 不跳过请求
|
||||
*/
|
||||
fun HttpServletRequest.isSkipRequest(ignorePaths: Array<String>): Boolean {
|
||||
return isSkipRequest(this.method, this.requestURI, ignorePaths)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否跳过请求
|
||||
* @apiNote 这个方法是给请求id过滤器使用的
|
||||
* @return true: 跳过请求; false: 不跳过请求
|
||||
*/
|
||||
fun ClientRequest.isSkipRequest(ignorePaths: Array<String>): Boolean {
|
||||
return isSkipRequest(this.method().name(), this.url().path, ignorePaths)
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否跳过请求
|
||||
* @param method 请求方法
|
||||
* @param uri 请求路径
|
||||
* @return true: 跳过请求; false: 不跳过请求
|
||||
*/
|
||||
fun isSkipRequest(method: String, uri: String, ignorePaths: Array<String>): Boolean {
|
||||
return when {
|
||||
// 跳过 OPTIONS 请求
|
||||
HttpMethod.OPTIONS.name() == method -> true
|
||||
// 跳过 HEAD 请求
|
||||
HttpMethod.HEAD.name() == method -> true
|
||||
// 跳过 TRACE 请求
|
||||
HttpMethod.TRACE.name() == method -> true
|
||||
// 跳过模式匹配
|
||||
ignorePaths.any { uri.matches(Regex(it)) } -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.filter
|
||||
|
||||
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
|
||||
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
import org.springframework.web.server.WebFilter
|
||||
import org.springframework.web.server.WebFilterChain
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* 反应式请求 ID 过滤器
|
||||
*
|
||||
* 该类的主要作用是在每个请求开始时生成或获取一个唯一的请求 ID,并将其设置到日志上下文中,
|
||||
* 以便在后续的日志记录中能够追踪到该请求。它还支持基于特定模式跳过某些请求的处理。
|
||||
*
|
||||
* @param traceProperties 配置属性,包含请求 ID 的头名称和 MDK 关键等信息
|
||||
* @since 2025-02-09 02:14:49
|
||||
* @author gewuyou
|
||||
*/
|
||||
class ReactiveRequestIdFilter(
|
||||
private val traceProperties: TraceProperties
|
||||
) : WebFilter {
|
||||
override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
|
||||
val request = exchange.request
|
||||
// 检测请求是否需要跳过
|
||||
if (request.isSkipRequest(traceProperties.ignorePatten)) {
|
||||
return chain.filter(exchange)
|
||||
}
|
||||
// 获取请求头中请求 ID 的名称和 MDK 中的键
|
||||
val requestIdHeader = traceProperties.requestIdHeaderName
|
||||
val requestIdMdcKey = traceProperties.requestIdMdcKey
|
||||
// 尝试从请求头中获取 requestId,如果存在则设置到 RequestIdUtil 中,否则生成一个新的 requestId
|
||||
request.headers[requestIdHeader]?.let {
|
||||
it.firstOrNull()?.let(RequestIdUtil::requestId::set) ?: RequestIdUtil.generateRequestId()
|
||||
} ?: RequestIdUtil.generateRequestId()
|
||||
// 获取当前的 requestId
|
||||
val currentRequestId = RequestIdUtil.requestId
|
||||
// 将 requestId 设置到日志中
|
||||
MDC.put(requestIdMdcKey, currentRequestId)
|
||||
log.info("设置 Request id: $currentRequestId")
|
||||
// ✅ **创建新的 request 并更新 exchange**
|
||||
// 更新请求头,确保后续的请求处理中包含 requestId
|
||||
val mutatedRequest = request.mutate()
|
||||
.header(requestIdHeader, currentRequestId)
|
||||
.build()
|
||||
val mutatedExchange = exchange.mutate().request(mutatedRequest).build()
|
||||
// 放行请求
|
||||
return chain.filter(mutatedExchange)
|
||||
// ✅ 让 Reactor 线程也能获取 requestId
|
||||
// 将 requestId 写入 Reactor 的上下文中,以便在异步处理中也能访问
|
||||
.contextWrite { ctx -> ctx.put(requestIdMdcKey, currentRequestId!!) }
|
||||
.doFinally {
|
||||
// 清理 MDC 中的 requestId,避免内存泄漏
|
||||
MDC.remove(requestIdMdcKey)
|
||||
// 将 requestId 清除
|
||||
RequestIdUtil.removeRequestId()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.filter
|
||||
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
|
||||
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.web.filter.OncePerRequestFilter
|
||||
|
||||
/**
|
||||
* 请求过滤器
|
||||
*
|
||||
* 该过滤器用于在请求处理过程中添加和管理请求ID(RequestId),以便于日志追踪和调试
|
||||
* 它基于Spring的OncePerRequestFilter,确保每个请求只被过滤一次
|
||||
*
|
||||
* @param traceProperties Trace属性配置,包含请求ID的相关配置信息
|
||||
* @since 2025-01-02 14:31:07
|
||||
* @author gewuyou
|
||||
*/
|
||||
class RequestIdFilter(
|
||||
private val traceProperties: TraceProperties
|
||||
) : OncePerRequestFilter() {
|
||||
override fun doFilterInternal(
|
||||
request: HttpServletRequest,
|
||||
response: HttpServletResponse,
|
||||
chain: FilterChain
|
||||
) {
|
||||
// 检查请求是否需要跳过
|
||||
if (request.isSkipRequest(traceProperties.ignorePatten)) {
|
||||
return chain.doFilter(request, response)
|
||||
}
|
||||
// 获取请求头中 requestId 的名称和 MDC 中的键
|
||||
val requestIdHeader = traceProperties.requestIdHeaderName
|
||||
val requestIdMdcKey = traceProperties.requestIdMdcKey
|
||||
try {
|
||||
// 尝试从请求头中获取 requestId
|
||||
request.getHeader(requestIdHeader)?.also(
|
||||
RequestIdUtil::requestId::set
|
||||
) ?: run {
|
||||
// 如果没有,则生成新的 requestId
|
||||
RequestIdUtil.generateRequestId()
|
||||
}
|
||||
// 获取 requestId
|
||||
val requestId = RequestIdUtil.requestId
|
||||
// 将requestId 设置到日志中
|
||||
MDC.put(requestIdMdcKey, requestId)
|
||||
log.info("设置 Request id: $requestId")
|
||||
// 将 requestId 设置到响应头中
|
||||
response.setHeader(requestIdHeader, requestId)
|
||||
// 继续处理请求
|
||||
chain.doFilter(request, response)
|
||||
} finally {
|
||||
// 移除 MDC 中的 requestId
|
||||
MDC.remove(requestIdMdcKey)
|
||||
// 清理当前线程的 RequestId,防止内存泄漏
|
||||
RequestIdUtil.removeRequestId()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.filter
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.extension.isSkipRequest
|
||||
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
|
||||
import org.slf4j.MDC
|
||||
import org.springframework.web.reactive.function.client.ClientRequest
|
||||
import org.springframework.web.reactive.function.client.ClientResponse
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction
|
||||
import org.springframework.web.reactive.function.client.ExchangeFunction
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* Web客户端请求ID过滤器
|
||||
*
|
||||
* 该类的主要作用是在发出HTTP请求时,确保请求ID的正确传递和记录
|
||||
* 如果请求被忽略,则直接传递不做处理;否则,会尝试从请求头中获取请求ID,
|
||||
* 如果获取不到则生成新的请求ID,并将其设置到请求头中以及日志中,以便于追踪请求
|
||||
*
|
||||
* @param traceProperties 追踪属性配置,包括忽略模式、请求ID头名称和MDK中的键
|
||||
* @since 2025-05-02 22:18:06
|
||||
* @author gewuyou
|
||||
*/
|
||||
class WebClientRequestIdFilter(
|
||||
private val traceProperties: TraceProperties
|
||||
) : ExchangeFilterFunction {
|
||||
override fun filter(request: ClientRequest, next: ExchangeFunction): Mono<ClientResponse> {
|
||||
// 检查请求是否被忽略,如果被忽略,则直接执行请求
|
||||
if (request.isSkipRequest(traceProperties.ignorePatten)) {
|
||||
return next.exchange(request)
|
||||
}
|
||||
// 获取请求头中请求 ID 的名称和 MDK 中的键
|
||||
val requestIdHeader = traceProperties.requestIdHeaderName
|
||||
val requestIdMdcKey = traceProperties.requestIdMdcKey
|
||||
// 尝试从请求头中获取 requestId,如果存在则设置到 RequestIdUtil 中,否则生成一个新的 requestId
|
||||
request.headers()[requestIdHeader]?.let {
|
||||
it.firstOrNull()?.let(RequestIdUtil::requestId::set) ?: RequestIdUtil.generateRequestId()
|
||||
} ?: RequestIdUtil.generateRequestId()
|
||||
// 获取当前的 requestId
|
||||
val currentRequestId = RequestIdUtil.requestId
|
||||
// 将 requestId 设置到日志中
|
||||
MDC.put(requestIdMdcKey, currentRequestId)
|
||||
log.info("设置 Request id: $currentRequestId")
|
||||
// 创建一个新的请求,包含 requestId 头
|
||||
val mutatedRequest = ClientRequest.from(request)
|
||||
.header(requestIdHeader, currentRequestId)
|
||||
.build()
|
||||
// 执行请求,并在请求完成后清除 MDC 和 RequestIdUtil 中的 requestId
|
||||
return next.exchange(mutatedRequest)
|
||||
.doFinally {
|
||||
MDC.remove(requestIdMdcKey)
|
||||
RequestIdUtil.removeRequestId()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.interceptor
|
||||
|
||||
|
||||
|
||||
import com.gewuyou.forgeboot.core.extension.log
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
import feign.RequestInterceptor
|
||||
import feign.RequestTemplate
|
||||
|
||||
/**
|
||||
* Feign请求ID 拦截器
|
||||
*
|
||||
* @since 2025-03-17 16:42:50
|
||||
* @author gewuyou
|
||||
*/
|
||||
class FeignRequestIdInterceptor(
|
||||
private val traceProperties: TraceProperties
|
||||
) : RequestInterceptor {
|
||||
override fun apply(template: RequestTemplate) {
|
||||
// 尝试获取当前请求的请求id
|
||||
val requestId = RequestIdUtil.requestId
|
||||
requestId?.let {
|
||||
// 如果请求id存在,则添加到请求头中
|
||||
template.header(traceProperties.requestIdHeaderName, requestId)
|
||||
} ?: run {
|
||||
log.warn("请求ID为null,请检查您是否已在过滤链中添加了请求filter。")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.provider
|
||||
|
||||
import com.gewuyou.forgeboot.context.impl.StringContextHolder
|
||||
import com.gewuyou.forgeboot.trace.api.RequestIdProvider
|
||||
import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
import com.gewuyou.forgeboot.trace.api.config.TraceProperties
|
||||
|
||||
|
||||
/**
|
||||
@ -10,7 +11,9 @@ import com.gewuyou.forgeboot.trace.impl.util.RequestIdUtil
|
||||
* @since 2025-05-03 17:26:46
|
||||
* @author gewuyou
|
||||
*/
|
||||
class TraceRequestIdProvider: RequestIdProvider {
|
||||
class TraceRequestIdProvider(
|
||||
private val traceProperties: TraceProperties
|
||||
): RequestIdProvider {
|
||||
/**
|
||||
* 获取请求ID
|
||||
*
|
||||
@ -19,6 +22,6 @@ class TraceRequestIdProvider: RequestIdProvider {
|
||||
* @return 请求ID的字符串表示
|
||||
*/
|
||||
override fun getRequestId(): String {
|
||||
return RequestIdUtil.requestId?:throw RuntimeException("requestId is null")
|
||||
return StringContextHolder.get(traceProperties.requestIdMdcKey) ?:throw RuntimeException("requestId is null")
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package com.gewuyou.forgeboot.trace.impl.util
|
||||
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 请求 ID Util
|
||||
* 这个类需配合 RequestIdFilter 使用,用于生成请求 ID,并将其绑定到线程变量中,供后续可能需要的地方使用。
|
||||
* @author gewuyou
|
||||
* @since 2025-01-02 14:27:45
|
||||
*/
|
||||
object RequestIdUtil {
|
||||
private val REQUEST_ID_HOLDER = ThreadLocal<String>()
|
||||
fun generateRequestId() {
|
||||
REQUEST_ID_HOLDER.set(UUID.randomUUID().toString())
|
||||
}
|
||||
|
||||
var requestId: String?
|
||||
get() = REQUEST_ID_HOLDER.get()
|
||||
set(uuid) {
|
||||
REQUEST_ID_HOLDER.set(uuid)
|
||||
}
|
||||
|
||||
fun removeRequestId() {
|
||||
REQUEST_ID_HOLDER.remove()
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user