Skip to content

Commit 4dd6b7b

Browse files
authored
Merge pull request #108 from CathalOConnorRH/MGDSTRM-3724
MGDSTRM-3724 Add the status code to the keycloak_request_duration_bucket metric
2 parents 2dcf302 + 7b60b74 commit 4dd6b7b

File tree

4 files changed

+126
-25
lines changed

4 files changed

+126
-25
lines changed

src/main/java/org/jboss/aerogear/keycloak/metrics/MetricsFilter.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,25 @@ public final class MetricsFilter implements ContainerRequestFilter, ContainerRes
2323
private static final String METRICS_REQUEST_TIMESTAMP = "metrics.requestTimestamp";
2424
private static final MetricsFilter INSTANCE = new MetricsFilter();
2525

26+
private static final boolean URI_METRICS_ENABLED = Boolean.parseBoolean(System.getenv("URI_METRICS_ENABLED"));
27+
2628
// relevant response content types to be measured
2729
private static final Set<MediaType> contentTypes = new HashSet<>();
30+
2831
static {
2932
contentTypes.add(MediaType.APPLICATION_JSON_TYPE);
3033
contentTypes.add(MediaType.APPLICATION_XML_TYPE);
3134
contentTypes.add(MediaType.TEXT_HTML_TYPE);
3235
}
36+
3337
private static final Set<MediaType> CONTENT_TYPES = Collections.unmodifiableSet(contentTypes);
3438

3539
public static MetricsFilter instance() {
3640
return INSTANCE;
3741
}
3842

39-
private MetricsFilter() { }
43+
private MetricsFilter() {
44+
}
4045

4146
@Override
4247
public void filter(ContainerRequestContext req) {
@@ -48,20 +53,31 @@ public void filter(ContainerRequestContext req, ContainerResponseContext res) {
4853
int status = res.getStatus();
4954

5055
String resource = ResourceExtractor.getResource(req.getUriInfo());
56+
String uri = ResourceExtractor.getURI(req.getUriInfo());
5157

52-
PrometheusExporter.instance().recordResponseTotal(status, req.getMethod(), resource);
53-
if (status >= 400) {
54-
PrometheusExporter.instance().recordResponseError(status, req.getMethod(), resource);
58+
if (URI_METRICS_ENABLED) {
59+
PrometheusExporter.instance().recordResponseTotal(status, req.getMethod(), resource, uri);
60+
if (status >= 400) {
61+
PrometheusExporter.instance().recordResponseError(status, req.getMethod(), resource, uri);
62+
}
63+
} else {
64+
PrometheusExporter.instance().recordResponseTotal(status, req.getMethod(), resource);
65+
if (status >= 400) {
66+
PrometheusExporter.instance().recordResponseError(status, req.getMethod(), resource);
67+
}
5568
}
56-
5769
// Record request duration if timestamp property is present
5870
// and only if it is relevant (skip pictures)
5971
if (req.getProperty(METRICS_REQUEST_TIMESTAMP) != null &&
6072
contentTypeIsRelevant(res)) {
6173
long time = (long) req.getProperty(METRICS_REQUEST_TIMESTAMP);
6274
long dur = System.currentTimeMillis() - time;
6375
LOG.trace("Duration is calculated as " + dur + " ms.");
64-
PrometheusExporter.instance().recordRequestDuration(dur, req.getMethod(), resource);
76+
if (URI_METRICS_ENABLED) {
77+
PrometheusExporter.instance().recordRequestDuration(status, dur, req.getMethod(), resource, uri);
78+
} else {
79+
PrometheusExporter.instance().recordRequestDuration(status, dur, req.getMethod(), resource);
80+
}
6581
}
6682
}
6783

src/main/java/org/jboss/aerogear/keycloak/metrics/PrometheusExporter.java

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public final class PrometheusExporter {
3737

3838
private final static String PROMETHEUS_PUSHGATEWAY_GROUPINGKEY_INSTANCE = "PROMETHEUS_GROUPING_KEY_INSTANCE";
3939
private final static Pattern PROMETHEUS_PUSHGATEWAY_GROUPINGKEY_INSTANCE_ENVVALUE_PATTERN = Pattern.compile("ENVVALUE:(.+?)");
40-
40+
4141
private final static String PROMETHEUS_PUSHGATEWAY_JOB = "PROMETHEUS_PUSHGATEWAY_JOB";
4242

4343
private static PrometheusExporter INSTANCE;
@@ -149,24 +149,46 @@ private PrometheusExporter() {
149149
.labelNames("realm", "provider", "error", "client_id")
150150
.register();
151151

152-
responseTotal = Counter.build()
152+
final boolean URI_METRICS_ENABLED = Boolean.parseBoolean(System.getenv("URI_METRICS_ENABLED"));
153+
if (URI_METRICS_ENABLED){
154+
responseTotal = Counter.build()
155+
.name("keycloak_response_total")
156+
.help("Total number of responses")
157+
.labelNames("code", "method", "resource", "uri")
158+
.register();
159+
160+
responseErrors = Counter.build()
161+
.name("keycloak_response_errors")
162+
.help("Total number of error responses")
163+
.labelNames("code", "method", "resource", "uri")
164+
.register();
165+
166+
requestDuration = Histogram.build()
167+
.name("keycloak_request_duration")
168+
.help("Request duration")
169+
.buckets(50, 100, 250, 500, 1000, 2000, 10000, 30000)
170+
.labelNames("code", "method", "resource", "uri")
171+
.register();
172+
} else {
173+
responseTotal = Counter.build()
153174
.name("keycloak_response_total")
154175
.help("Total number of responses")
155176
.labelNames("code", "method", "resource")
156177
.register();
157178

158-
responseErrors = Counter.build()
179+
responseErrors = Counter.build()
159180
.name("keycloak_response_errors")
160181
.help("Total number of error responses")
161182
.labelNames("code", "method", "resource")
162183
.register();
163184

164-
requestDuration = Histogram.build()
185+
requestDuration = Histogram.build()
165186
.name("keycloak_request_duration")
166187
.help("Request duration")
167188
.buckets(50, 100, 250, 500, 1000, 2000, 10000, 30000)
168-
.labelNames("method", "resource")
189+
.labelNames("code", "method", "resource")
169190
.register();
191+
}
170192

171193
// Counters for all user events
172194
for (EventType type : EventType.values()) {
@@ -367,8 +389,30 @@ public void recordCodeToTokenError(final Event event) {
367389
* @param amt The duration in milliseconds
368390
* @param method HTTP method of the request
369391
*/
370-
public void recordRequestDuration(double amt, String method, String resource) {
371-
requestDuration.labels(method, resource).observe(amt);
392+
public void recordRequestDuration(int code, double amt, String method, String resource, String uri) {
393+
requestDuration.labels(Integer.toString(code), method, resource, uri).observe(amt);
394+
pushAsync();
395+
}
396+
397+
/**
398+
* Record the duration between one request and response
399+
*
400+
* @param amt The duration in milliseconds
401+
* @param method HTTP method of the request
402+
*/
403+
public void recordRequestDuration(int code, double amt, String method, String resource) {
404+
requestDuration.labels(Integer.toString(code), method, resource).observe(amt);
405+
pushAsync();
406+
}
407+
408+
/**
409+
* Increase the response total count by a given method and response code
410+
*
411+
* @param code The returned http status code
412+
* @param method The request method used
413+
*/
414+
public void recordResponseTotal(int code, String method, String resource, String uri) {
415+
responseTotal.labels(Integer.toString(code), method, resource, uri).inc();
372416
pushAsync();
373417
}
374418

@@ -383,6 +427,17 @@ public void recordResponseTotal(int code, String method, String resource) {
383427
pushAsync();
384428
}
385429

430+
/**
431+
* Increase the response error count by a given method and response code
432+
*
433+
* @param code The returned http status code
434+
* @param method The request method used
435+
*/
436+
public void recordResponseError(int code, String method, String resource, String uri) {
437+
responseErrors.labels(Integer.toString(code), method, resource, uri).inc();
438+
pushAsync();
439+
}
440+
386441
/**
387442
* Increase the response error count by a given method and response code
388443
*

src/main/java/org/jboss/aerogear/keycloak/metrics/ResourceExtractor.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
import javax.ws.rs.core.UriInfo;
66
import java.util.List;
7+
import java.util.regex.*;
78

89
class ResourceExtractor {
910

1011
private final static Logger logger = Logger.getLogger(ResourceExtractor.class);
1112

1213
private static final boolean IS_RESOURCE_SCRAPING_DISABLED = Boolean.getBoolean("RESOURCE_SCRAPING_DISABLED");
13-
14+
private static final boolean URI_METRICS_ENABLED = Boolean.getBoolean("URI_METRICS_ENABLED");
15+
private static final boolean URI_METRICS_DETAILED = Boolean.getBoolean("URI_METRICS_DETAILED");
1416

1517
private ResourceExtractor() {
1618
}
@@ -35,12 +37,12 @@ private ResourceExtractor() {
3537
* @param uriInfo {@link UriInfo} object obtained from JAX-RS
3638
* @return The resource name.
3739
*/
38-
static String getResource(UriInfo uriInfo) {
40+
static String getResource(UriInfo uriInfo) {
3941
if (!IS_RESOURCE_SCRAPING_DISABLED) {
4042
List<String> matchedURIs = uriInfo.getMatchedURIs();
4143
if (matchedURIs.size() >= 2) {
4244
// A special case for all static resources - we're not interested in
43-
// evey particular resource - just an aggregate with all other endpoints.
45+
// every particular resource - just an aggregate with all other endpoints.
4446
if ("resources".equals(matchedURIs.get(matchedURIs.size() - 1))) {
4547
return "";
4648
}
@@ -54,4 +56,29 @@ static String getResource(UriInfo uriInfo) {
5456
return "";
5557
}
5658

59+
/**
60+
* This method obtains a list of resource info from the {@link UriInfo} object and returns the resource URI.
61+
* @param uriInfo {@link UriInfo} object obtained from JAX-RS
62+
* @return The resource uri.
63+
*/
64+
static String getURI(UriInfo uriInfo) {
65+
if (URI_METRICS_ENABLED) {
66+
List<String> matchedURIs = uriInfo.getMatchedURIs();
67+
StringBuilder sb = new StringBuilder();
68+
if (matchedURIs.get(0).contains("/token"))
69+
{
70+
String uri = matchedURIs.get(0);
71+
72+
if(URI_METRICS_DETAILED) {
73+
sb.append(uri);
74+
} else {
75+
String[] realm = uri.split("/");
76+
uri=uri.replace(realm[1], "{realm}");
77+
sb.append(uri);
78+
}
79+
}
80+
return sb.toString();
81+
}
82+
return "";
83+
}
5784
}

src/test/java/org/jboss/aerogear/keycloak/metrics/PrometheusExporterTest.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -265,28 +265,31 @@ public void shouldCorrectlyRecordGenericAdminEvents() throws IOException {
265265

266266
@Test
267267
public void shouldCorrectlyRecordResponseDurations() throws IOException {
268-
PrometheusExporter.instance().recordRequestDuration(5, "GET", "admin,admin/serverinfo");
268+
environmentVariables.set("URI_METRICS_ENABLED", "true");
269+
PrometheusExporter.instance().recordRequestDuration(200, 5, "GET", "admin,admin/serverinfo", "auth/realm");
269270
assertGenericMetric("keycloak_request_duration_count", 1,
270-
tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"));
271+
tuple("code","200"), tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"), tuple("uri", "auth/realm"));
271272
assertGenericMetric("keycloak_request_duration_sum", 5,
272-
tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"));
273+
tuple("code","200"), tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"), tuple("uri", "auth/realm"));
273274
}
274275

275276
@Test
276277
public void shouldCorrectlyRecordResponseTotal() throws IOException {
277-
PrometheusExporter.instance().recordResponseTotal(200, "GET", "admin,admin/serverinfo");
278-
PrometheusExporter.instance().recordResponseTotal(500, "POST", "admin,admin/serverinfo");
278+
environmentVariables.set("URI_METRICS_ENABLED", "true");
279+
PrometheusExporter.instance().recordResponseTotal(200, "GET", "admin,admin/serverinfo", "auth/realm");
280+
PrometheusExporter.instance().recordResponseTotal(500, "POST", "admin,admin/serverinfo", "auth/realm");
279281
assertGenericMetric("keycloak_response_total", 1,
280-
tuple("code", "200"), tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"));
282+
tuple("code", "200"), tuple("method", "GET"), tuple("resource", "admin,admin/serverinfo"), tuple("uri", "auth/realm"));
281283
assertGenericMetric("keycloak_response_total", 1,
282-
tuple("code", "500"), tuple("method", "POST"), tuple("resource", "admin,admin/serverinfo"));
284+
tuple("code", "500"), tuple("method", "POST"), tuple("resource", "admin,admin/serverinfo"), tuple("uri", "auth/realm"));
283285
}
284286

285287
@Test
286288
public void shouldCorrectlyRecordResponseErrors() throws IOException {
287-
PrometheusExporter.instance().recordResponseError(500, "POST", "admin,admin/serverinfo");
289+
environmentVariables.set("URI_METRICS_ENABLED", "true");
290+
PrometheusExporter.instance().recordResponseError(500, "POST", "admin,admin/serverinfo", "auth/realm");
288291
assertGenericMetric("keycloak_response_errors", 1,
289-
tuple("code", "500"), tuple("method", "POST"), tuple("resource", "admin,admin/serverinfo"));
292+
tuple("code", "500"), tuple("method", "POST"), tuple("resource", "admin,admin/serverinfo"), tuple("uri", "auth/realm"));
290293
}
291294

292295
@Test

0 commit comments

Comments
 (0)