Skip to content

Commit

Permalink
Refactor for easier way to report 4xx status codes
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanik committed Sep 21, 2017
1 parent 4820174 commit b47eca1
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 116 deletions.
Expand Up @@ -38,15 +38,15 @@ public class MetricsQueue {
public static final int MAX_TIME = 3000;

private ConcurrentLinkedDeque<RequestMetric> queue;
private Map<Integer, RequestMetricSummary> statistics;
private Map<StatusCodeGroup, RequestMetricSummary> statistics;

public MetricsQueue() {
this(null,null);
}

@JsonCreator
public MetricsQueue(@JsonProperty("lastRequests") ConcurrentLinkedDeque<RequestMetric> queue,
@JsonProperty("detailed") Map<Integer, RequestMetricSummary> statistics) {
@JsonProperty("detailed") Map<StatusCodeGroup, RequestMetricSummary> statistics) {
this.queue = ofNullable(queue).orElse(new ConcurrentLinkedDeque<>());
this.statistics = ofNullable(statistics).orElse(new ConcurrentHashMap<>());
}
Expand All @@ -58,7 +58,7 @@ public boolean offer(RequestMetric metric) {
queue.removeLast();
}

Integer statusCode = metric.getStatusCode();
StatusCodeGroup statusCode = StatusCodeGroup.valueOf(metric.getStatusCode());
if (!statistics.containsKey(statusCode)) {
statistics.putIfAbsent(statusCode, new RequestMetricSummary());
}
Expand All @@ -72,7 +72,7 @@ public boolean offer(RequestMetric metric) {
return true;
}

public Map<Integer, RequestMetricSummary> getDetailed() {
public Map<StatusCodeGroup, RequestMetricSummary> getDetailed() {
return statistics;
}

Expand Down
@@ -0,0 +1,46 @@
/*
* ****************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2017] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
* ****************************************************************************
*/

package org.cloudfoundry.identity.uaa.metrics;

public enum StatusCodeGroup {
INFORMATIONAL("1xx",1),
SUCCESS("2xx",2),
REDIRECT("3xx",3),
CLIENT_ERROR("4xx",4),
SERVER_ERROR("5xx",5);

private final String name;
private final int value;

StatusCodeGroup(String name, int value) {
this.name = name;
this.value = value;
}

public String getName() {
return name;
}

public static StatusCodeGroup valueOf(int statusCode) {
int seriesCode = statusCode / 100;
for (StatusCodeGroup series : values()) {
if (series.value == seriesCode) {
return series;
}
}
throw new IllegalArgumentException("No matching constant for [" + statusCode + "]");
}
}
Expand Up @@ -14,6 +14,8 @@

import com.timgroup.statsd.StatsDClient;
import org.cloudfoundry.identity.uaa.metrics.MetricsQueue;
import org.cloudfoundry.identity.uaa.metrics.StatusCodeGroup;
import org.cloudfoundry.identity.uaa.metrics.RequestMetricSummary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.spel.standard.SpelExpressionParser;
Expand All @@ -24,11 +26,14 @@
import java.util.LinkedHashMap;
import java.util.Map;

import static java.util.Optional.ofNullable;

public class UaaMetricsEmitter {
private final StatsDClient statsDClient;
private final MBeanServerConnection server;
@Autowired
private MetricsUtils metricsUtils;
private RequestMetricSummary MISSING_METRICS = new RequestMetricSummary(0l, 0d, 0l, 0d, 0l, 0d, 0l, 0d);

public UaaMetricsEmitter(StatsDClient statsDClient, MBeanServerConnection server) {
this.statsDClient = statsDClient;
Expand Down Expand Up @@ -66,11 +71,21 @@ public void emitGlobalRequestMetrics() throws Exception {
if (json != null) {
MetricsQueue globals = JsonUtils.readValue(json, MetricsQueue.class);
String prefix = "requests.global.";
statsDClient.gauge(prefix + "completed.time", (long) globals.getTotals().getAverageTime());
statsDClient.gauge(prefix + "completed.count", globals.getTotals().getCount());
RequestMetricSummary totals = globals.getTotals();
statsDClient.gauge(prefix + "completed.time", (long) totals.getAverageTime());
statsDClient.gauge(prefix + "completed.count", totals.getCount());
//unhealthy
statsDClient.gauge(prefix + "unhealthy.count", globals.getTotals().getIntolerableCount());
statsDClient.gauge(prefix + "unhealthy.time", (long)globals.getTotals().getAverageIntolerableTime());
statsDClient.gauge(prefix + "unhealthy.count", totals.getIntolerableCount());
statsDClient.gauge(prefix + "unhealthy.time", (long) totals.getAverageIntolerableTime());
//status codes
for (StatusCodeGroup family : StatusCodeGroup.values()) {
RequestMetricSummary summary =
ofNullable(globals.getDetailed().get(family))
.orElse(MISSING_METRICS);

statsDClient.gauge(prefix + "status_"+family.getName()+".count", summary.getCount());
}

}
}
//server statistics
Expand Down
Expand Up @@ -86,11 +86,16 @@ public void requestCount_metrics_emitted() throws Exception {
Mockito.when(metricsUtils.pullUpMap("cloudfoundry.identity", "*", server)).thenReturn((Map)mBeanMap3);

uaaMetricsEmitter.emitGlobalRequestMetrics();
Mockito.verify(statsDClient).gauge("requests.global.completed.count", 18l);
Mockito.verify(statsDClient).gauge("requests.global.completed.time", 67l);
Mockito.verify(statsDClient).gauge("requests.global.completed.count", 3087l);
Mockito.verify(statsDClient).gauge("requests.global.completed.time", 29l);
Mockito.verify(statsDClient).gauge("server.inflight.count", 3l);
Mockito.verify(statsDClient).gauge("requests.global.unhealthy.count", 1l);
Mockito.verify(statsDClient).gauge("requests.global.unhealthy.time", (long)3.25);
Mockito.verify(statsDClient).gauge("requests.global.unhealthy.time", 4318l);
Mockito.verify(statsDClient).gauge("requests.global.status_1xx.count", 0l);
Mockito.verify(statsDClient).gauge("requests.global.status_2xx.count", 2148l);
Mockito.verify(statsDClient).gauge("requests.global.status_3xx.count", 763l);
Mockito.verify(statsDClient).gauge("requests.global.status_4xx.count", 175l);
Mockito.verify(statsDClient).gauge("requests.global.status_5xx.count", 1l);
}

@Test
Expand Down Expand Up @@ -127,75 +132,95 @@ public void test(Collection<?> c) {
" {\n" +
" \"uri\":\"/uaa/\",\n" +
" \"statusCode\":302,\n" +
" \"requestStartTime\":1505967590958,\n" +
" \"requestCompleteTime\":1505967590982,\n" +
" \"requestStartTime\":1506021406240,\n" +
" \"requestCompleteTime\":1506021406260,\n" +
" \"nrOfDatabaseQueries\":1,\n" +
" \"databaseQueryTime\":0\n" +
" },\n" +
" {\n" +
" \"uri\":\"/uaa/login\",\n" +
" \"statusCode\":200,\n" +
" \"requestStartTime\":1505967590991,\n" +
" \"requestCompleteTime\":1505967591648,\n" +
" \"requestStartTime\":1506021406265,\n" +
" \"requestCompleteTime\":1506021406970,\n" +
" \"nrOfDatabaseQueries\":12,\n" +
" \"databaseQueryTime\":1\n" +
" \"databaseQueryTime\":0\n" +
" },\n" +
" {\n" +
" \"uri\":\"/uaa/\",\n" +
" \"statusCode\":302,\n" +
" \"requestStartTime\":1505967591913,\n" +
" \"requestCompleteTime\":1505967591918,\n" +
" \"requestStartTime\":1506021407210,\n" +
" \"requestCompleteTime\":1506021407216,\n" +
" \"nrOfDatabaseQueries\":1,\n" +
" \"databaseQueryTime\":0\n" +
" \"databaseQueryTime\":1\n" +
" },\n" +
" {\n" +
" \"uri\":\"/uaa/login\",\n" +
" \"statusCode\":200,\n" +
" \"requestStartTime\":1505967591921,\n" +
" \"requestCompleteTime\":1505967591977,\n" +
" \"requestStartTime\":1506021407224,\n" +
" \"requestCompleteTime\":1506021407284,\n" +
" \"nrOfDatabaseQueries\":12,\n" +
" \"databaseQueryTime\":0\n" +
" },\n" +
" {\n" +
" \"uri\":\"/uaa/vendor/font-awesome/css/font-awesome.min.css\",\n" +
" \"statusCode\":200,\n" +
" \"requestStartTime\":1505967591982,\n" +
" \"requestCompleteTime\":1505967592031,\n" +
" \"uri\":\"/uaa/resources/oss/stylesheets/application.css\",\n" +
" \"statusCode\":304,\n" +
" \"requestStartTime\":1506021407293,\n" +
" \"requestCompleteTime\":1506021407331,\n" +
" \"nrOfDatabaseQueries\":1,\n" +
" \"databaseQueryTime\":0\n" +
" }\n" +
" ],\n" +
" \"detailed\":{\n" +
" \"200\":{\n" +
" \"count\":14,\n" +
" \"averageTime\":74.21428571428572,\n" +
" \"intolerableCount\":1,\n" +
" \"averageIntolerableTime\":3.25,\n" +
" \"databaseQueryCount\":113,\n" +
" \"averageDatabaseQueryTime\":0.03539823008849556,\n" +
" \"SERVER_ERROR\":{\n" +
" \"count\":1,\n" +
" \"averageTime\":87.0,\n" +
" \"intolerableCount\":0,\n" +
" \"averageIntolerableTime\":0.0,\n" +
" \"databaseQueryCount\":13,\n" +
" \"averageDatabaseQueryTime\":0.0,\n" +
" \"databaseFailedQueryCount\":0,\n" +
" \"averageDatabaseFailedQueryTime\":0.0\n" +
" },\n" +
" \"302\":{\n" +
" \"count\":4,\n" +
" \"averageTime\":46.0,\n" +
" \"REDIRECT\":{\n" +
" \"count\":763,\n" +
" \"averageTime\":35.86107470511138,\n" +
" \"intolerableCount\":1,\n" +
" \"averageIntolerableTime\":4318.0,\n" +
" \"databaseQueryCount\":5428,\n" +
" \"averageDatabaseQueryTime\":0.028002947678703018,\n" +
" \"databaseFailedQueryCount\":188,\n" +
" \"averageDatabaseFailedQueryTime\":0.047872340425531915\n" +
" },\n" +
" \"SUCCESS\":{\n" +
" \"count\":2148,\n" +
" \"averageTime\":28.867318435754207,\n" +
" \"intolerableCount\":0,\n" +
" \"averageIntolerableTime\":0.0,\n" +
" \"databaseQueryCount\":30,\n" +
" \"averageDatabaseQueryTime\":0.03333333333333333,\n" +
" \"databaseFailedQueryCount\":0,\n" +
" \"averageDatabaseFailedQueryTime\":0.0\n" +
" \"databaseQueryCount\":77513,\n" +
" \"averageDatabaseQueryTime\":0.0341362094100345,\n" +
" \"databaseFailedQueryCount\":17327,\n" +
" \"averageDatabaseFailedQueryTime\":0.057136261326253886\n" +
" },\n" +
" \"CLIENT_ERROR\":{\n" +
" \"count\":175,\n" +
" \"averageTime\":15.097142857142877,\n" +
" \"intolerableCount\":0,\n" +
" \"averageIntolerableTime\":0.0,\n" +
" \"databaseQueryCount\":843,\n" +
" \"averageDatabaseQueryTime\":0.021352313167259794,\n" +
" \"databaseFailedQueryCount\":34,\n" +
" \"averageDatabaseFailedQueryTime\":0.058823529411764705\n" +
" }\n" +
" },\n" +
" \"summary\":{\n" +
" \"count\":18,\n" +
" \"averageTime\":67.94444444444446,\n" +
" \"intolerableCount\":0,\n" +
" \"averageIntolerableTime\":0.0,\n" +
" \"databaseQueryCount\":143,\n" +
" \"averageDatabaseQueryTime\":0.034965034965034954,\n" +
" \"databaseFailedQueryCount\":0,\n" +
" \"averageDatabaseFailedQueryTime\":0.0\n" +
" \"count\":3087,\n" +
" \"averageTime\":29.834143181081966,\n" +
" \"intolerableCount\":1,\n" +
" \"averageIntolerableTime\":4318.0,\n" +
" \"databaseQueryCount\":83797,\n" +
" \"averageDatabaseQueryTime\":0.033605021659486665,\n" +
" \"databaseFailedQueryCount\":17549,\n" +
" \"averageDatabaseFailedQueryTime\":0.05704028719585168\n" +
" }\n" +
"}";
}

0 comments on commit b47eca1

Please sign in to comment.