87public final class GlowrootJettyHandler
extends Handler.Wrapper {
89 private final Set<String> healthPaths;
90 private final Map<String, Long> thresholdByNormalizedPath;
91 private final long defaultThresholdMs;
92 private final String requestIdHeader;
94 private final Function<Object, String> userExtractor;
96 private GlowrootJettyHandler(
final Handler next,
final Builder
builder) {
98 healthPaths = Set.copyOf(
builder.healthPaths);
99 thresholdByNormalizedPath = Map.copyOf(
builder.thresholds);
100 defaultThresholdMs =
builder.defaultThresholdMs;
101 requestIdHeader =
builder.requestIdHeader;
102 requestIdGenerator =
builder.requestIdGenerator;
103 userExtractor =
builder.userExtractor;
108 return new Builder();
112 public boolean handle(
final Request request,
final Response response,
final Callback callback)
throws Exception {
113 final var method = request.getMethod();
114 final var path = request.getHttpURI() !=
null ? request.getHttpURI().getPath() :
null;
115 final var normalized = PathNormalizer.normalize(path);
117 setTransactionIdentity(method, path, normalized);
118 applySlowThreshold(path, normalized);
119 captureRequestId(request);
122 final var result = super.handle(request, response, callback);
123 captureResponseStatus(response);
124 captureAuthenticatedUser(request);
126 }
catch (
final Throwable t) {
133 private void setTransactionIdentity(
final String method,
final String path,
final String normalized) {
135 Glowroot.setTransactionType(
"Web");
136 Glowroot.setTransactionName(method +
" " + normalized);
137 Glowroot.addTransactionAttribute(
"http.method", method);
138 Glowroot.addTransactionAttribute(
"http.path", path ==
null ?
"unknown" : path);
139 Glowroot.addTransactionAttribute(
"http.normalized_path", normalized);
140 }
catch (
final Throwable ignore) {
145 private void applySlowThreshold(
final String path,
final String normalized) {
146 if (path !=
null && healthPaths.contains(path)) {
148 Glowroot.setTransactionSlowThreshold(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
149 }
catch (
final Throwable ignore) {
153 final var threshold = thresholdByNormalizedPath.getOrDefault(normalized, defaultThresholdMs);
156 Glowroot.setTransactionSlowThreshold(threshold, TimeUnit.MILLISECONDS);
157 }
catch (
final Throwable ignore) {
163 private void captureRequestId(
final Request request) {
164 if (requestIdHeader ==
null) {
168 var reqId = request.getHeaders().get(requestIdHeader);
169 if ((reqId ==
null || reqId.isBlank()) && requestIdGenerator !=
null) {
170 reqId = requestIdGenerator.nextId();
172 if (reqId !=
null && !reqId.isBlank()) {
173 Glowroot.addTransactionAttribute(
"request.id", reqId);
175 }
catch (
final Throwable ignore) {
180 private void captureResponseStatus(
final Response response) {
182 final var status = response.getStatus();
184 Glowroot.addTransactionAttribute(
"http.status", String.valueOf(status));
185 Glowroot.addTransactionAttribute(
"http.status_class", status / 100 +
"xx");
187 }
catch (
final Throwable ignore) {
192 private void captureAuthenticatedUser(
final Request request) {
193 if (userExtractor ==
null) {
197 final var ctx = request.getAttribute(JettyAuthHandler.REQ_ATTR_AUTH);
199 final var user = userExtractor.apply(ctx);
200 if (user !=
null && !user.isBlank()) {
201 Glowroot.setTransactionUser(user);
202 Glowroot.addTransactionAttribute(
"auth.user", user);
205 }
catch (
final Throwable ignore) {
210 private void captureError(
final Throwable t) {
212 Glowroot.addTransactionAttribute(
"error", t.getClass().getName());
213 Glowroot.addTransactionAttribute(
"error.message", t.getMessage() ==
null ?
"" : t.getMessage());
214 }
catch (
final Throwable ignore) {
220 public static final class Builder {
222 private final Set<String> healthPaths =
new HashSet<>();
223 private final Map<String, Long> thresholds =
new HashMap<>();
224 private long defaultThresholdMs = 2_000L;
225 private String requestIdHeader =
null;
226 private RequestIdGenerator requestIdGenerator =
null;
227 private Function<Object, String> userExtractor =
null;
236 public Builder healthPath(
final String path) {
237 healthPaths.add(path);
242 public Builder healthPaths(
final String... paths) {
243 Collections.addAll(healthPaths, paths);
251 public Builder slowThreshold(
final String normalizedPath,
final long thresholdMs) {
252 thresholds.put(normalizedPath, thresholdMs);
260 public Builder defaultSlowThreshold(
final long thresholdMs) {
261 defaultThresholdMs = thresholdMs;
270 public Builder requestIdHeader(
final String header) {
271 requestIdHeader = header;
272 requestIdGenerator =
null;
280 public Builder requestIdHeader(
final String header,
final boolean generateIfAbsent) {
281 requestIdHeader = header;
282 requestIdGenerator = generateIfAbsent ?
new GlowrootRequestIdGenerator() : null;
290 public Builder requestIdHeader(
final String header,
final RequestIdGenerator requestIdGenerator) {
291 requestIdHeader = header;
292 this.requestIdGenerator = requestIdGenerator;
315 public Builder userExtractor(
final Function<Object, String> extractor) {
316 userExtractor = extractor;
333 public GlowrootJettyHandler wrap(
final Handler next) {
334 return new GlowrootJettyHandler(next,
this);