Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
GlowrootSlowThresholdMiddleware.java
Go to the documentation of this file.
1package dev.rafex.ether.glowroot.jetty12;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.concurrent.TimeUnit;
6
7import org.glowroot.agent.api.Glowroot;
8
9/*-
10 * #%L
11 * ether-glowroot-jetty12
12 * %%
13 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
14 * %%
15 * Permission is hereby granted, free of charge, to any person obtaining a copy
16 * of this software and associated documentation files (the "Software"), to deal
17 * in the Software without restriction, including without limitation the rights
18 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 * copies of the Software, and to permit persons to whom the Software is
20 * furnished to do so, subject to the following conditions:
21 *
22 * The above copyright notice and this permission notice shall be included in
23 * all copies or substantial portions of the Software.
24 *
25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 * THE SOFTWARE.
32 * #L%
33 */
34
35import dev.rafex.ether.http.core.HttpHandler;
36import dev.rafex.ether.http.core.Middleware;
37
38/**
39 * Middleware that applies per-route slow-transaction thresholds in Glowroot.
40 *
41 * <p>
42 * Different endpoints have different latency characteristics — a PDF export is
43 * naturally slower than an auth check. This middleware lets you specify
44 * distinct thresholds so Glowroot's slow-trace alerts are meaningful for every
45 * route, not just a single global value.
46 * </p>
47 *
48 * <p>
49 * Route matching uses {@link PathNormalizer} so patterns with path variables
50 * (e.g. {@code /users/:id}) match any concrete URL like
51 * {@code /users/550e8400-…}.
52 * </p>
53 *
54 * <p>
55 * Build with the fluent {@link Builder}:
56 * </p>
57 *
58 * <pre>{@code
59 * middlewareRegistry.add(GlowrootSlowThresholdMiddleware.builder().defaultThreshold(2_000) // 2 s for everything else
60 * .threshold("/api/export/:id", 30_000) // 30 s for exports
61 * .threshold("/api/auth/login", 500) // 500 ms for login
62 * .build());
63 * }</pre>
64 */
65public final class GlowrootSlowThresholdMiddleware implements Middleware {
66
67 private final Map<String, Long> thresholdByNormalizedPath;
68 private final long defaultThresholdMs;
69
70 private GlowrootSlowThresholdMiddleware(final Map<String, Long> thresholds, final long defaultThresholdMs) {
71 this.thresholdByNormalizedPath = Map.copyOf(thresholds);
72 this.defaultThresholdMs = defaultThresholdMs;
73 }
74
75 /** Returns a new {@link Builder}. */
76 public static Builder builder() {
77 return new Builder();
78 }
79
80 @Override
81 public HttpHandler wrap(final HttpHandler next) {
82 return exchange -> {
83 final var normalized = PathNormalizer.normalize(exchange.path());
84 final var threshold = thresholdByNormalizedPath.getOrDefault(normalized, defaultThresholdMs);
85 if (threshold > 0) {
86 try {
87 Glowroot.setTransactionSlowThreshold(threshold, TimeUnit.MILLISECONDS);
88 } catch (final Throwable ignore) {
89 // Glowroot agent not present; do not affect request
90 }
91 }
92 return next.handle(exchange);
93 };
94 }
95
96 /* ── Builder ── */
97
98 public static final class Builder {
99
100 private final Map<String, Long> thresholds = new HashMap<>();
101 private long defaultThresholdMs = 2_000L;
102
103 private Builder() {
104 }
105
106 /**
107 * Registers a slow threshold for an exact normalized path.
108 *
109 * @param normalizedPath path with placeholders, e.g. {@code "/users/:id"}
110 * @param thresholdMs threshold in milliseconds
111 */
112 public Builder threshold(final String normalizedPath, final long thresholdMs) {
113 thresholds.put(normalizedPath, thresholdMs);
114 return this;
115 }
116
117 /**
118 * Sets the default threshold applied to paths not explicitly configured.
119 * Default is {@code 2000} ms.
120 */
121 public Builder defaultThreshold(final long thresholdMs) {
122 this.defaultThresholdMs = thresholdMs;
123 return this;
124 }
125
126 public GlowrootSlowThresholdMiddleware build() {
127 return new GlowrootSlowThresholdMiddleware(thresholds, defaultThresholdMs);
128 }
129 }
130}
boolean handle(HttpExchange exchange)