From ee4de63b0adb1109b44c8ff054049fefae78e6a9 Mon Sep 17 00:00:00 2001 From: davdarras Date: Mon, 19 Aug 2024 17:14:36 +0200 Subject: [PATCH] feat: add trace propagation --- pom.xml | 2 +- .../application/configuration/CorsConfig.java | 2 +- .../auth/OidcSecurityConfiguration.java | 2 + .../configuration/log/LogInterceptor.java | 44 +++++++++++++++++-- .../rest/RestTemplateTraceIdInterceptor.java | 26 +++++++++++ 5 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 queen-application/src/main/java/fr/insee/queen/application/configuration/rest/RestTemplateTraceIdInterceptor.java diff --git a/pom.xml b/pom.xml index 37458a44..d5f0ba15 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ Modules for queen back-office - 4.3.15 + 4.3.16 21 21 diff --git a/queen-application/src/main/java/fr/insee/queen/application/configuration/CorsConfig.java b/queen-application/src/main/java/fr/insee/queen/application/configuration/CorsConfig.java index c91fc011..59a66c6c 100644 --- a/queen-application/src/main/java/fr/insee/queen/application/configuration/CorsConfig.java +++ b/queen-application/src/main/java/fr/insee/queen/application/configuration/CorsConfig.java @@ -18,7 +18,7 @@ protected CorsConfigurationSource corsConfigurationSource(ApplicationProperties CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOriginPatterns(applicationProperties.corsOrigins()); configuration.setAllowedMethods(List.of("GET", "PUT", "POST", "DELETE", "OPTIONS", "PATCH")); - configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "Access-Control-Allow-Origin")); + configuration.setAllowedHeaders(List.of("Authorization", "Content-Type", "Access-Control-Allow-Origin", "Traceparent", "Tracestate")); configuration.addExposedHeader("Content-Disposition"); configuration.setMaxAge(3600L); configuration.setAllowCredentials(true); diff --git a/queen-application/src/main/java/fr/insee/queen/application/configuration/auth/OidcSecurityConfiguration.java b/queen-application/src/main/java/fr/insee/queen/application/configuration/auth/OidcSecurityConfiguration.java index dc457f25..e304db98 100644 --- a/queen-application/src/main/java/fr/insee/queen/application/configuration/auth/OidcSecurityConfiguration.java +++ b/queen-application/src/main/java/fr/insee/queen/application/configuration/auth/OidcSecurityConfiguration.java @@ -5,6 +5,7 @@ import fr.insee.queen.application.configuration.properties.RoleProperties; import fr.insee.queen.application.configuration.rest.RestTemplateAddJsonHeaderInterceptor; import fr.insee.queen.application.configuration.rest.RestTemplateTokenInterceptor; +import fr.insee.queen.application.configuration.rest.RestTemplateTraceIdInterceptor; import fr.insee.queen.application.web.authentication.AuthenticationHelper; import lombok.AllArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -98,6 +99,7 @@ protected RestTemplate restTemplatePilotage(AuthenticationHelper authenticationH RestTemplate restTemplate = new RestTemplate(); restTemplate.getInterceptors().add(new RestTemplateAddJsonHeaderInterceptor()); restTemplate.getInterceptors().add(new RestTemplateTokenInterceptor(authenticationHelper)); + restTemplate.getInterceptors().add(new RestTemplateTraceIdInterceptor()); return restTemplate; } } diff --git a/queen-application/src/main/java/fr/insee/queen/application/configuration/log/LogInterceptor.java b/queen-application/src/main/java/fr/insee/queen/application/configuration/log/LogInterceptor.java index fb979710..16dc79a6 100644 --- a/queen-application/src/main/java/fr/insee/queen/application/configuration/log/LogInterceptor.java +++ b/queen-application/src/main/java/fr/insee/queen/application/configuration/log/LogInterceptor.java @@ -4,6 +4,7 @@ import jakarta.annotation.Nonnull; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; @@ -13,16 +14,23 @@ import org.springframework.web.servlet.ModelAndView; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Component @RequiredArgsConstructor @Slf4j public class LogInterceptor implements HandlerInterceptor { + + private static final Pattern TRACE_PATTERN = Pattern.compile( + "^\\d{2}-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$" + ); private final AuthenticationHelper authenticationHelper; @Override - public boolean preHandle(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull Object handler) { - String fishTag = UUID.randomUUID().toString(); + public boolean preHandle(@NonNull HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull Object handler) { + + String traceIdentifier = extractTraceIdentifier(request); String method = request.getMethod(); String operationPath = request.getRequestURI(); @@ -30,7 +38,7 @@ public boolean preHandle(HttpServletRequest request, @Nonnull HttpServletRespons String userId = authentication.getName(); - MDC.put("id", fishTag); + MDC.put("id", traceIdentifier); MDC.put("path", operationPath); MDC.put("method", method); MDC.put("user", userId); @@ -50,4 +58,34 @@ public void afterCompletion(@Nonnull HttpServletRequest request, @Nonnull HttpSe Exception exception) { MDC.clear(); } + + /** + * @param request current http servlet request + * @return a trace identifier for the current request + */ + private String extractTraceIdentifier(HttpServletRequest request) { + String traceParent = request.getHeader("traceparent"); + + if (!isTraceValid(traceParent)) { + String version = "00"; + String traceId = UUID.randomUUID().toString().replace("-", ""); + String spanId = traceId.substring(16); + String traceFlags = "01"; + traceParent = String.format("%s-%s-%s-%s", version, traceId, spanId, traceFlags); + } + return traceParent; + } + + /** + * Check trace identifier validity + * @param traceIdentifier identifier to check + * @return true if valid, false otherwise + */ + private boolean isTraceValid(String traceIdentifier) { + if (traceIdentifier == null) { + return false; + } + Matcher matcher = TRACE_PATTERN.matcher(traceIdentifier); + return matcher.matches(); + } } \ No newline at end of file diff --git a/queen-application/src/main/java/fr/insee/queen/application/configuration/rest/RestTemplateTraceIdInterceptor.java b/queen-application/src/main/java/fr/insee/queen/application/configuration/rest/RestTemplateTraceIdInterceptor.java new file mode 100644 index 00000000..8475b73c --- /dev/null +++ b/queen-application/src/main/java/fr/insee/queen/application/configuration/rest/RestTemplateTraceIdInterceptor.java @@ -0,0 +1,26 @@ +package fr.insee.queen.application.configuration.rest; + +import org.slf4j.MDC; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; + +import java.io.IOException; + +public class RestTemplateTraceIdInterceptor implements ClientHttpRequestInterceptor { + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte [] body, + ClientHttpRequestExecution execution) throws IOException { + // Retrieve the Trace Identifier from MDC + String traceIdentifier = MDC.get("id"); + + if (traceIdentifier != null) { + // Add the Trace ID to the outgoing request's headers + request.getHeaders().add("traceparent", traceIdentifier); + } + + return execution.execute(request, body); + } +}