Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
GlowrootRequestIdMiddleware.java
Go to the documentation of this file.
1package dev.rafex.ether.glowroot.jetty12;
2
3import java.util.Objects;
4import java.util.function.Function;
5
6import org.glowroot.agent.api.Glowroot;
7
8/*-
9 * #%L
10 * ether-glowroot-jetty12
11 * %%
12 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
13 * %%
14 * Permission is hereby granted, free of charge, to any person obtaining a copy
15 * of this software and associated documentation files (the "Software"), to deal
16 * in the Software without restriction, including without limitation the rights
17 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 * copies of the Software, and to permit persons to whom the Software is
19 * furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
30 * THE SOFTWARE.
31 * #L%
32 */
33
34import dev.rafex.ether.http.core.HttpExchange;
35import dev.rafex.ether.http.core.HttpHandler;
36import dev.rafex.ether.http.core.Middleware;
37import dev.rafex.ether.observability.core.request.RequestIdGenerator;
38
39/**
40 * Middleware that records a correlation request ID as a Glowroot attribute.
41 *
42 * <p>
43 * Extracts the request ID via a configurable {@code idExtractor} function. If
44 * no ID is found and {@code generateIfAbsent} is {@code true}, a random UUID is
45 * generated. The resulting ID is stored under the {@code request.id}
46 * transaction attribute, enabling correlation between Glowroot traces and
47 * external log aggregators (Loki, ELK, etc.).
48 * </p>
49 *
50 * <p>
51 * For Jetty-specific extraction from the {@code X-Request-Id} header use
52 * {@link GlowrootJettyExtractors#xRequestId()} as the extractor:
53 * </p>
54 *
55 * <pre>{@code
56 * middlewareRegistry.add(new GlowrootRequestIdMiddleware(GlowrootJettyExtractors.xRequestId(), true));
57 * }</pre>
58 */
59public final class GlowrootRequestIdMiddleware implements Middleware {
60
61 private final Function<HttpExchange, String> idExtractor;
62 private final RequestIdGenerator requestIdGenerator;
63
64 public GlowrootRequestIdMiddleware(final Function<HttpExchange, String> idExtractor,
65 final boolean generateIfAbsent) {
66 this(idExtractor, generateIfAbsent ? new GlowrootRequestIdGenerator() : null);
67 }
68
69 public GlowrootRequestIdMiddleware(final Function<HttpExchange, String> idExtractor,
70 final RequestIdGenerator requestIdGenerator) {
71 this.idExtractor = Objects.requireNonNull(idExtractor, "idExtractor must not be null");
72 this.requestIdGenerator = requestIdGenerator;
73 }
74
75 @Override
76 public HttpHandler wrap(final HttpHandler next) {
77 return exchange -> {
78 try {
79 String requestId = idExtractor.apply(exchange);
80 if ((requestId == null || requestId.isBlank()) && requestIdGenerator != null) {
81 requestId = requestIdGenerator.nextId();
82 }
83 if (requestId != null && !requestId.isBlank()) {
84 Glowroot.addTransactionAttribute("request.id", requestId);
85 }
86 } catch (final Throwable ignore) {
87 // Extractor or Glowroot failure must never affect the request
88 }
89 return next.handle(exchange);
90 };
91 }
92}
RequestIdGenerator bridge that records generated request IDs into Glowroot transaction attributes.
GlowrootRequestIdMiddleware(final Function< HttpExchange, String > idExtractor, final RequestIdGenerator requestIdGenerator)
GlowrootRequestIdMiddleware(final Function< HttpExchange, String > idExtractor, final boolean generateIfAbsent)
boolean handle(HttpExchange exchange)