1

I'm using Spring Boot 3.5.7 with Micrometer Tracing (OpenTelemetry bridge) to add a traceId to my logs. I have a REST endpoint and a custom filter like this:

@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthRestController {

    private final PaymentService paymentService;

    @PostMapping("/token")
    public String generateToken(@RequestBody TokenRequest request) throws Exception {
        return paymentService.createToken(request);
    }
}


@Slf4j
@RequiredArgsConstructor
public class RedisJwtAuthenticationFilter extends OncePerRequestFilter {

    private final PaymentSessionService paymentSessionService;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain)
            throws ServletException, IOException {

        String txn = request.getHeader("X-Transaction-Id");
        log.info("FILTER THREAD = {}", Thread.currentThread().getName());

        if (StringUtils.isNotBlank(txn)) {
            if (!paymentSessionService.sessionExists(txn)) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }
        }

        filterChain.doFilter(request, response);
    }
}

public boolean sessionExists(final String txn) {
    log.info("REDIS THREAD = {}", Thread.currentThread().getName());

    final String key = ENDA_TXN + txn;
    try {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    } catch (DataAccessException e) {
        throw new RedisUnavailableException("Redis is unavailable while checking the session", e);
    }
}

@Bean
@Order(2)
public SecurityFilterChain filterChain(HttpSecurity http,
                                       ApplicationProperties applicationProperties,
                                       AesGcmJwtDecoder aesGcmJwtDecoder,
                                       JwtAuthenticationConverter jwtAuthenticationConverter,
                                       RedisJwtAuthenticationFilter redisJwtAuthenticationFilter) throws Exception {
    http
            .cors(withDefaults())
            .csrf(AbstractHttpConfigurer::disable)
            .addFilterAfter(redisJwtAuthenticationFilter, SecurityContextHolderFilter.class)
            .headers(headers ->
                    headers
                            .contentSecurityPolicy(csp -> csp.policyDirectives(applicationProperties.getSecurity().getContentSecurityPolicy()))
                            .frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin)
                            .referrerPolicy(referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN))
                            .permissionsPolicyHeader(permissions ->
                                    permissions.policy("camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()")
                            )
            )
            .authorizeHttpRequests(authz -> authz
                    .anyRequest().authenticated()
            )
            .sessionManagement(mgmt -> mgmt.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .oauth2ResourceServer(oauth2 -> oauth2
                    .jwt(jwt -> jwt
                            .decoder(aesGcmJwtDecoder)
                            .jwtAuthenticationConverter(jwtAuthenticationConverter)
                    )
            );

    return http.build();
}

Logs example:

2025-11-19 09:47:31.611 [traceId: a5dba50bef617c87a] [http-nio-9008-exec-3] AuthRestController - Finish execution

2025-11-19 09:47:31.629 [traceId: c2aa664e25e67da2] [http-nio-9008-exec-4] RedisJwtAuthenticationFilter - FILTER THREAD

As you can see:

The controller executes on http-nio-9008-exec-3

The filter executes on http-nio-9008-exec-4

Because of this thread change, Micrometer generates a new traceId, and I can't track the transaction across logs.

Notes:

I'm using Jedis / StringRedisTemplate in the filter (synchronous, blocking)

I do not use @Async or CompletableFuture

WebClient calls are .block()ed

I also have @EnableAspectJAutoProxy(proxyTargetClass = true) but I don't think it causes this

Question:

Why does the thread change between the controller and my filter in Tomcat?

How can I keep the same thread (or propagate the same traceId) across the filter and the controller for correct tracing?

1
  • It is a new request, thus a new trace. So unless you have something setup to include the traceId as header/param in your webclient it will not be propagated. Commented 2 days ago

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.