diff --git a/gateleen-expansion/README_expansion.md b/gateleen-expansion/README_expansion.md
index 2afb4053e..b057390c5 100644
--- a/gateleen-expansion/README_expansion.md
+++ b/gateleen-expansion/README_expansion.md
@@ -120,4 +120,28 @@ For more information about the StorageExpand feature see the [vertx-rest-storage
This allows you to create one octet-stream containing each json resource in the given collection (see expand feature).
Basically it works exactly the same way as the default expand feature works, except that it does not set an eTag for the request.
-> Attention: No eTag header is created / returned when this feature is used!
\ No newline at end of file
+> Attention: No eTag header is created / returned when this feature is used!
+
+### Micrometer metrics
+The expansion feature is monitored with micrometer. The following metrics are available:
+* gateleen_expand_requests_total
+* gateleen_storage_expand_requests_total
+
+For `expand_requests_total` additional tags are provided to specify the expand level.
+
+Example metrics:
+
+```
+# HELP gateleen_expand_requests_total
+# TYPE gateleen_expand_requests_total counter
+gateleen_expand_requests_total{level="1",} 23677.0
+gateleen_expand_requests_total{level="2",} 2350.0
+gateleen_expand_requests_total{level="3",} 77.0
+gateleen_expand_requests_total{level="4",} 0.0
+gateleen_expand_requests_total{level="0",} 0.0
+# HELP gateleen_storage_expand_requests_total
+# TYPE gateleen_storage_expand_requests_total counter
+gateleen_storage_expand_requests_total 37.0
+```
+
+To enable the metrics, set a `MeterRegistry` instance by calling `setMeterRegistry(MeterRegistry meterRegistry)` method in `ExpansionHandler` class.
\ No newline at end of file
diff --git a/gateleen-expansion/pom.xml b/gateleen-expansion/pom.xml
index 805289355..565ac09d1 100644
--- a/gateleen-expansion/pom.xml
+++ b/gateleen-expansion/pom.xml
@@ -22,6 +22,10 @@
org.swisspush
rest-storage
+
+ io.micrometer
+ micrometer-core
+
diff --git a/gateleen-expansion/src/main/java/org/swisspush/gateleen/expansion/ExpansionHandler.java b/gateleen-expansion/src/main/java/org/swisspush/gateleen/expansion/ExpansionHandler.java
index 59f309d43..09408a85b 100755
--- a/gateleen-expansion/src/main/java/org/swisspush/gateleen/expansion/ExpansionHandler.java
+++ b/gateleen-expansion/src/main/java/org/swisspush/gateleen/expansion/ExpansionHandler.java
@@ -1,5 +1,7 @@
package org.swisspush.gateleen.expansion;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
@@ -27,10 +29,7 @@
import org.swisspush.gateleen.routing.RuleFeaturesProvider;
import org.swisspush.gateleen.routing.RuleProvider;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.swisspush.gateleen.core.util.StatusCode.INTERNAL_SERVER_ERROR;
@@ -89,6 +88,10 @@ public class ExpansionHandler implements RuleChangesObserver {
private static final int DECREMENT_BY_ONE = 1;
private static final int MAX_RECURSION_LEVEL = 0;
+ private static final String EXPAND_REQUEST_METRIC = "gateleen.expand.requests";
+ private static final String STORAGE_EXPAND_REQUEST_METRIC = "gateleen.storage.expand.requests";
+ private static final String LEVEL = "level";
+
public static final String MAX_EXPANSION_LEVEL_SOFT_PROPERTY = "max.expansion.level.soft";
public static final String MAX_EXPANSION_LEVEL_HARD_PROPERTY = "max.expansion.level.hard";
public static final String MAX_SUBREQUEST_PROPERTY = "max.expansion.subrequests";
@@ -123,6 +126,9 @@ public class ExpansionHandler implements RuleChangesObserver {
private RuleFeaturesProvider ruleFeaturesProvider = new RuleFeaturesProvider(new ArrayList<>());
+ private final Map counterMap = new HashMap<>();
+ private Counter storageExpandCounter;
+
/**
* Creates a new instance of the ExpansionHandler.
*
@@ -176,6 +182,26 @@ public int getMaxSubRequestCount() {
return maxSubRequestCount;
}
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ counterMap.clear();
+ if(meterRegistry != null) {
+ counterMap.put(0, Counter.builder(EXPAND_REQUEST_METRIC).tag(LEVEL, "0").register(meterRegistry));
+ counterMap.put(1, Counter.builder(EXPAND_REQUEST_METRIC).tag(LEVEL, "1").register(meterRegistry));
+ counterMap.put(2, Counter.builder(EXPAND_REQUEST_METRIC).tag(LEVEL, "2").register(meterRegistry));
+ counterMap.put(3, Counter.builder(EXPAND_REQUEST_METRIC).tag(LEVEL, "3").register(meterRegistry));
+ counterMap.put(4, Counter.builder(EXPAND_REQUEST_METRIC).tag(LEVEL, "4").register(meterRegistry));
+
+ storageExpandCounter = Counter.builder(STORAGE_EXPAND_REQUEST_METRIC).register(meterRegistry);
+ }
+ }
+
+ private void incrementExpandReqCount(int level) {
+ Counter counter = counterMap.get(level);
+ if(counter != null) {
+ counter.increment();
+ }
+ }
+
/**
* Initialize the lists which defines, when which parameter
* is removed (if any).
@@ -363,6 +389,8 @@ private void handleExpansionRequest(final HttpServerRequest req, final Recursive
log.debug("constructed uri for request: {}", targetUri);
Integer finalExpandLevel = expandLevel;
+ incrementExpandReqCount(finalExpandLevel);
+
httpClient.request(HttpMethod.GET, targetUri).onComplete(asyncReqResult -> {
if (asyncReqResult.failed()) {
log.warn("Failed request to {}: {}", targetUri, asyncReqResult.cause());
@@ -505,6 +533,11 @@ private void makeStorageExpandRequest(final String targetUri, final List subReso
Logger log = RequestLoggerFactory.getLogger(ExpansionHandler.class, req);
HttpMethod reqMethod = HttpMethod.POST;
String reqUri = targetUri + "?storageExpand=true";
+
+ if(storageExpandCounter != null) {
+ storageExpandCounter.increment();
+ }
+
httpClient.request(reqMethod, reqUri).onComplete(asyncResult -> {
if (asyncResult.failed()) {
log.warn("Failed request to {}", reqUri, asyncResult.cause());
diff --git a/gateleen-expansion/src/test/java/org/swisspush/gateleen/expansion/ExpansionHandlerTest.java b/gateleen-expansion/src/test/java/org/swisspush/gateleen/expansion/ExpansionHandlerTest.java
index daef5a283..bb365d1ed 100644
--- a/gateleen-expansion/src/test/java/org/swisspush/gateleen/expansion/ExpansionHandlerTest.java
+++ b/gateleen-expansion/src/test/java/org/swisspush/gateleen/expansion/ExpansionHandlerTest.java
@@ -46,7 +46,6 @@ public class ExpansionHandlerTest {
public void setUp() {
vertx = Vertx.vertx();
httpClient = Mockito.mock(HttpClient.class);
- // Mockito.when(httpClient.request(any(HttpMethod.class), anyString(), Matchers.>any())).thenReturn(Mockito.mock(HttpClientRequest.class));
storage = new MockResourceStorage();
}
diff --git a/gateleen-hook/README_hook.md b/gateleen-hook/README_hook.md
index 62a863ed7..9ecc6849f 100644
--- a/gateleen-hook/README_hook.md
+++ b/gateleen-hook/README_hook.md
@@ -163,7 +163,7 @@ PUT http://myserver:7012/gateleen/everything/_hooks/listeners/http/myexample
"destination": "/gateleen/example/thePosition",
"filter": "/gateleen/everything/.*/position.*",
"headers": [
- { "header":"X-Expire-After", "value":"3600", mode:"complete"}
+ { "header":"X-Expire-After", "value":"3600", "mode":"complete"}
],
"headersFilter": "x-foo: (A|B)"
}
@@ -294,4 +294,22 @@ The response contains the matching routes, or an empty list if no match is found
{
"routes": []
}
-```
\ No newline at end of file
+```
+
+## Micrometer metrics
+The hook feature is monitored with micrometer. The following metrics are available:
+* gateleen_listener_count
+* gateleen_routes_count
+
+Example metrics:
+
+```
+# HELP gateleen_listener_count Amount of listener hooks currently registered
+# TYPE gateleen_listener_count gauge
+gateleen_listener_count 577.0
+# HELP gateleen_routes_count Amount of route hooks currently registered
+# TYPE gateleen_routes_count gauge
+gateleen_routes_count 15.0
+```
+
+To enable the metrics, set a `MeterRegistry` instance by calling `setMeterRegistry(MeterRegistry meterRegistry)` method in `HookHandler` class.
\ No newline at end of file
diff --git a/gateleen-hook/pom.xml b/gateleen-hook/pom.xml
index 3e75cb6b2..5931d2bc6 100644
--- a/gateleen-hook/pom.xml
+++ b/gateleen-hook/pom.xml
@@ -17,6 +17,10 @@
gateleen-queue
${project.version}
+
+ io.micrometer
+ micrometer-core
+
diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java
index 6999ed589..c0f42fff0 100755
--- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java
+++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/HookHandler.java
@@ -5,6 +5,8 @@
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import com.networknt.schema.ValidationMessage;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
@@ -59,6 +61,7 @@
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
@@ -150,6 +153,10 @@ public class HookHandler implements LoggableResource {
private final String normalizedRouteBase;
private final String normalizedListenerBase;
+ private final AtomicLong listenerCount = new AtomicLong(0);
+ private final AtomicLong routesCount = new AtomicLong(0);
+ private MeterRegistry meterRegistry;
+
/**
* Creates a new HookHandler.
*
@@ -162,7 +169,7 @@ public class HookHandler implements LoggableResource {
* @param hookRootUri hookRootUri
*/
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage storage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri) {
this(vertx, selfClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, userProfilePath, hookRootUri,
new QueueClient(vertx, monitoringHandler));
@@ -182,14 +189,14 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage sto
* @param requestQueue requestQueue
*/
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage storage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue) {
this(vertx, selfClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, userProfilePath, hookRootUri,
requestQueue, false);
}
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage storage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue, boolean listableRoutes) {
this(vertx, selfClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, userProfilePath, hookRootUri,
requestQueue, false, null);
@@ -210,7 +217,7 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage sto
* @param reducedPropagationManager reducedPropagationManager
*/
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage storage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue, boolean listableRoutes,
@Nullable ReducedPropagationManager reducedPropagationManager) {
this(vertx, selfClient, storage, loggingResourceManager, logAppenderRepository, monitoringHandler, userProfilePath, hookRootUri,
@@ -218,7 +225,7 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage sto
}
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage userProfileStorage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue, boolean listableRoutes,
ReducedPropagationManager reducedPropagationManager, @Nullable Handler doneHandler, ResourceStorage hookStorage) {
this(vertx, selfClient, userProfileStorage, loggingResourceManager, logAppenderRepository, monitoringHandler, userProfilePath, hookRootUri,
@@ -226,7 +233,7 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage use
}
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage userProfileStorage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue, boolean listableRoutes,
ReducedPropagationManager reducedPropagationManager, @Nullable Handler doneHandler, ResourceStorage hookStorage,
int routeMultiplier) {
@@ -256,7 +263,7 @@ public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage use
* parallel operation.
*/
public HookHandler(Vertx vertx, HttpClient selfClient, final ResourceStorage userProfileStorage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
String userProfilePath, String hookRootUri, RequestQueue requestQueue, boolean listableRoutes,
ReducedPropagationManager reducedPropagationManager, @Nullable Handler doneHandler, ResourceStorage hookStorage,
int routeMultiplier, @Nonnull QueueSplitter queueSplitter) {
@@ -317,6 +324,16 @@ public void handle(Void aVoid) {
initMethods.forEach(handlerConsumer -> handlerConsumer.accept(readyHandler));
}
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ if(meterRegistry != null) {
+ Gauge.builder("gateleen.listener.count", listenerCount, AtomicLong::get)
+ .description("Amount of listener hooks currently registered").register(meterRegistry);
+ Gauge.builder("gateleen.routes.count", routesCount, AtomicLong::get)
+ .description("Amount of route hooks currently registered").register(meterRegistry);
+ }
+ }
+
@Override
public void enableResourceLogging(boolean resourceLoggingEnabled) {
this.logHookConfigurationResourceChanges = resourceLoggingEnabled;
@@ -361,8 +378,15 @@ private void registerCleanupHandler(Handler readyHandler) {
routeRepository.removeRoute(key);
}
}
- monitoringHandler.updateListenerCount(listenerRepository.size());
- monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ if(meterRegistry != null) {
+ listenerCount.set(listenerRepository.size());
+ routesCount.set(routeRepository.getRoutes().size());
+ }
+
+ if(monitoringHandler != null) {
+ monitoringHandler.updateListenerCount(listenerRepository.size());
+ monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ }
log.trace("done");
});
@@ -1352,7 +1376,12 @@ private void unregisterRoute(String requestUrl) {
log.debug("Unregister route {}", routedUrl);
routeRepository.removeRoute(routedUrl);
- monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ if(meterRegistry != null) {
+ routesCount.set(routeRepository.getRoutes().size());
+ }
+ if(monitoringHandler != null) {
+ monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ }
}
/**
@@ -1367,7 +1396,12 @@ private void unregisterListener(String requestUrl) {
routeRepository.removeRoute(hookRootUri + LISTENER_HOOK_TARGET_PATH + getListenerUrlSegment(requestUrl));
listenerRepository.removeListener(listenerId);
- monitoringHandler.updateListenerCount(listenerRepository.size());
+ if(meterRegistry != null) {
+ listenerCount.set(listenerRepository.size());
+ }
+ if(monitoringHandler != null) {
+ monitoringHandler.updateListenerCount(listenerRepository.size());
+ }
}
/**
@@ -1491,7 +1525,12 @@ private void registerListener(Buffer buffer) {
// create and add a new listener (or update an already existing listener)
listenerRepository.addListener(new Listener(listenerId, getMonitoredUrlSegment(requestUrl), target, hook));
- monitoringHandler.updateListenerCount(listenerRepository.size());
+ if(meterRegistry != null) {
+ listenerCount.set(listenerRepository.size());
+ }
+ if(monitoringHandler != null) {
+ monitoringHandler.updateListenerCount(listenerRepository.size());
+ }
}
/**
@@ -1672,7 +1711,12 @@ private void registerRoute(Buffer buffer) {
existingRoute.getRule().setHeaderFunction(hook.getHeaderFunction());
existingRoute.getHook().setExpirationTime(hook.getExpirationTime().orElse(null));
}
- monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ if(meterRegistry != null) {
+ routesCount.set(routeRepository.getRoutes().size());
+ }
+ if(monitoringHandler != null) {
+ monitoringHandler.updateRoutesCount(routeRepository.getRoutes().size());
+ }
}
/**
@@ -1721,8 +1765,10 @@ private boolean headersFilterPatternEquals(Pattern headersFilterPatternLeft, Pat
* @return Route
*/
private Route createRoute(String urlPattern, HttpHook hook, String hookDisplayText) {
- return new Route(vertx, userProfileStorage, loggingResourceManager, logAppenderRepository, monitoringHandler,
+ Route route = new Route(vertx, userProfileStorage, loggingResourceManager, logAppenderRepository, monitoringHandler,
userProfilePath, hook, urlPattern, selfClient, hookDisplayText);
+ route.setMeterRegistry(meterRegistry);
+ return route;
}
/**
diff --git a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java
index 2aba542ca..d7068c2a0 100755
--- a/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java
+++ b/gateleen-hook/src/main/java/org/swisspush/gateleen/hook/Route.java
@@ -1,6 +1,8 @@
package org.swisspush.gateleen.hook;
-import io.vertx.codegen.annotations.Nullable;
+import javax.annotation.Nullable;
+
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
@@ -80,7 +82,7 @@ public class Route {
* @param urlPattern - this can be a listener or a normal urlPattern (eg. for a route)
*/
public Route(Vertx vertx, ResourceStorage storage, LoggingResourceManager loggingResourceManager,
- LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler, String userProfilePath,
+ LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler, String userProfilePath,
HttpHook httpHook, String urlPattern, HttpClient selfClient, String hookDisplayText) {
this.vertx = vertx;
this.storage = storage;
@@ -100,6 +102,10 @@ public Route(Vertx vertx, ResourceStorage storage, LoggingResourceManager loggin
createForwarder();
}
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ forwarder.setMeterRegistry(meterRegistry);
+ }
+
/**
* Creates the forwarder for this hook.
*/
diff --git a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueClient.java b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueClient.java
index c85c94848..7251ffbde 100755
--- a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueClient.java
+++ b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueClient.java
@@ -13,6 +13,8 @@
import org.swisspush.gateleen.core.util.StatusCode;
import org.swisspush.gateleen.monitoring.MonitoringHandler;
+import javax.annotation.Nullable;
+
import static org.swisspush.redisques.util.RedisquesAPI.*;
/**
@@ -32,7 +34,7 @@ public class QueueClient implements RequestQueue {
* @param vertx vertx
* @param monitoringHandler monitoringHandler
*/
- public QueueClient(Vertx vertx, MonitoringHandler monitoringHandler) {
+ public QueueClient(Vertx vertx, @Nullable MonitoringHandler monitoringHandler) {
this.vertx = vertx;
this.monitoringHandler = monitoringHandler;
}
@@ -109,7 +111,7 @@ public void lockedEnqueue(HttpRequest queuedRequest, String queue, String lockRe
vertx.eventBus().request(getRedisquesAddress(), buildLockedEnqueueOperation(queue,
queuedRequest.toJsonObject().put(QUEUE_TIMESTAMP, System.currentTimeMillis()).encode(), lockRequestedBy),
(Handler>>) event -> {
- if (OK.equals(event.result().body().getString(STATUS))) {
+ if (OK.equals(event.result().body().getString(STATUS)) && monitoringHandler != null) {
monitoringHandler.updateLastUsedQueueSizeInformation(queue);
monitoringHandler.updateEnqueue();
}
@@ -172,8 +174,10 @@ public Future enqueueFuture(HttpRequest queuedRequest, String queue) {
queuedRequest.toJsonObject().put(QUEUE_TIMESTAMP, System.currentTimeMillis()).encode()),
(Handler>>) event -> {
if (OK.equals(event.result().body().getString(STATUS))) {
- monitoringHandler.updateLastUsedQueueSizeInformation(queue);
- monitoringHandler.updateEnqueue();
+ if(monitoringHandler != null) {
+ monitoringHandler.updateLastUsedQueueSizeInformation(queue);
+ monitoringHandler.updateEnqueue();
+ }
promise.complete();
} else {
promise.fail(event.result().body().getString(MESSAGE));
@@ -216,8 +220,10 @@ private void enqueue(final HttpServerRequest request, HttpRequest queuedRequest,
vertx.eventBus().request(getRedisquesAddress(), buildEnqueueOperation(queue, queuedRequest.toJsonObject().put(QUEUE_TIMESTAMP, System.currentTimeMillis()).encode()),
(Handler>>) event -> {
if (OK.equals(event.result().body().getString(STATUS))) {
- monitoringHandler.updateLastUsedQueueSizeInformation(queue);
- monitoringHandler.updateEnqueue();
+ if(monitoringHandler != null) {
+ monitoringHandler.updateLastUsedQueueSizeInformation(queue);
+ monitoringHandler.updateEnqueue();
+ }
if (request != null) {
ResponseStatusCodeLogUtil.info(request, StatusCode.ACCEPTED, QueueClient.class);
diff --git a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueProcessor.java b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueProcessor.java
index 7e2a8214a..8c68d077b 100755
--- a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueProcessor.java
+++ b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueueProcessor.java
@@ -25,6 +25,8 @@
import org.swisspush.gateleen.queue.queuing.circuitbreaker.util.QueueCircuitState;
import org.swisspush.gateleen.queue.queuing.circuitbreaker.util.QueueResponseType;
+import javax.annotation.Nullable;
+
import static io.vertx.core.Future.failedFuture;
import static io.vertx.core.Future.succeededFuture;
import static io.vertx.core.buffer.Buffer.buffer;
@@ -49,18 +51,18 @@ public class QueueProcessor {
private Logger log = LoggerFactory.getLogger(QueueProcessor.class);
- public QueueProcessor(final Vertx vertx, final HttpClient httpClient, final MonitoringHandler monitoringHandler) {
+ public QueueProcessor(final Vertx vertx, final HttpClient httpClient, @Nullable final MonitoringHandler monitoringHandler) {
this(vertx, httpClient, monitoringHandler, null);
}
- public QueueProcessor(final Vertx vertx, final HttpClient httpClient, final MonitoringHandler monitoringHandler, QueueCircuitBreaker queueCircuitBreaker) {
+ public QueueProcessor(final Vertx vertx, final HttpClient httpClient, @Nullable final MonitoringHandler monitoringHandler, QueueCircuitBreaker queueCircuitBreaker) {
this(vertx, httpClient, monitoringHandler, queueCircuitBreaker, newGateleenThriftyExceptionFactory(), true);
}
public QueueProcessor(
Vertx vertx,
HttpClient httpClient,
- MonitoringHandler monitoringHandler,
+ @Nullable MonitoringHandler monitoringHandler,
QueueCircuitBreaker queueCircuitBreaker,
GateleenExceptionFactory exceptionFactory,
boolean immediatelyStartQueueProcessing
@@ -275,7 +277,9 @@ private void executeQueuedRequest(Message message, Logger logger, Ht
}
message.reply(new JsonObject().put(STATUS, OK));
performCircuitBreakerActions(queueName, queuedRequest, SUCCESS, state);
- monitoringHandler.updateDequeue();
+ if(monitoringHandler != null) {
+ monitoringHandler.updateDequeue();
+ }
} else if (QueueRetryUtil.retryQueueItem(queuedRequest.getHeaders(), statusCode, logger)) {
logger.info("Failed queued request to {}: {} {}", queuedRequest.getUri(), statusCode, response.statusMessage());
message.reply(new JsonObject().put(STATUS, ERROR).put(MESSAGE, statusCode + " " + response.statusMessage()));
diff --git a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueuingHandler.java b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueuingHandler.java
index e95c0face..00421e931 100755
--- a/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueuingHandler.java
+++ b/gateleen-queue/src/main/java/org/swisspush/gateleen/queue/queuing/QueuingHandler.java
@@ -14,6 +14,8 @@
import org.swisspush.gateleen.queue.queuing.splitter.NoOpQueueSplitter;
import org.swisspush.gateleen.queue.queuing.splitter.QueueSplitter;
+import javax.annotation.Nullable;
+
import static org.swisspush.redisques.util.RedisquesAPI.buildCheckOperation;
/**
@@ -24,6 +26,7 @@
public class QueuingHandler implements Handler {
public static final String QUEUE_HEADER = "x-queue";
+ public static final String ORIGINALLY_QUEUED_HEADER = "x-originally-queued";
public static final String DUPLICATE_CHECK_HEADER = "x-duplicate-check";
private final RequestQueue requestQueue;
@@ -42,7 +45,7 @@ public QueuingHandler(
Vertx vertx,
RedisProvider redisProvider,
HttpServerRequest request,
- MonitoringHandler monitoringHandler
+ @Nullable MonitoringHandler monitoringHandler
) {
this(vertx, redisProvider, request, new QueueClient(vertx, monitoringHandler), new NoOpQueueSplitter());
}
@@ -51,7 +54,7 @@ public QueuingHandler(
Vertx vertx,
RedisProvider redisProvider,
HttpServerRequest request,
- MonitoringHandler monitoringHandler,
+ @Nullable MonitoringHandler monitoringHandler,
QueueSplitter queueSplitter
) {
this(
@@ -84,6 +87,9 @@ public void handle(final Buffer buffer) {
// Remove the queue header to avoid feedback loop
headers.remove(QUEUE_HEADER);
+ // Add a header to indicate that this request was initially queued
+ headers.add(ORIGINALLY_QUEUED_HEADER, "true");
+
if (headers.names().contains(DUPLICATE_CHECK_HEADER)) {
DuplicateCheckHandler.checkDuplicateRequest(redisProvider, request.uri(), buffer, headers.get(DUPLICATE_CHECK_HEADER), requestIsDuplicate -> {
if (requestIsDuplicate) {
diff --git a/gateleen-queue/src/test/java/org/swisspush/gateleen/queue/queuing/QueueClientTest.java b/gateleen-queue/src/test/java/org/swisspush/gateleen/queue/queuing/QueueClientTest.java
index feb879e1b..de0675b47 100644
--- a/gateleen-queue/src/test/java/org/swisspush/gateleen/queue/queuing/QueueClientTest.java
+++ b/gateleen-queue/src/test/java/org/swisspush/gateleen/queue/queuing/QueueClientTest.java
@@ -69,6 +69,31 @@ public void testEnqueueFuture(TestContext context){
Mockito.verify(monitoringHandler, Mockito.timeout(1000).times(1)).updateEnqueue();
}
+ @Test
+ public void testEnqueueFutureNotUpdatingMonitoringHandlerWhenNotProvided(TestContext context){
+ Async async = context.async();
+
+ // create a QueueClient without monitoringHandler
+ queueClient = new QueueClient(vertx, null);
+
+ /*
+ * consume event bus messages directed to redisques and verify message content.
+ * reply with 'success' for enqueuing
+ */
+ vertx.eventBus().localConsumer(Address.redisquesAddress(), (Handler>) message -> {
+ validateMessage(context, message, QueueOperation.enqueue, "myQueue");
+ message.reply(new JsonObject().put(STATUS, OK));
+ });
+
+ HttpRequest request = new HttpRequest(HttpMethod.PUT, "/targetUri", MultiMap.caseInsensitiveMultiMap(), Buffer.buffer("{\"key\":\"value\"}").getBytes());
+ queueClient.enqueueFuture(request, "myQueue").onComplete(event -> {
+ context.assertTrue(event.succeeded());
+ async.complete();
+ });
+
+ Mockito.verifyNoInteractions(monitoringHandler);
+ }
+
@Test
public void testEnqueueFutureNotUpdatingMonitoringHandlerOnRedisquesFail(TestContext context){
Async async = context.async();
diff --git a/gateleen-routing/README_routing.md b/gateleen-routing/README_routing.md
index d89614afc..3a50831a8 100644
--- a/gateleen-routing/README_routing.md
+++ b/gateleen-routing/README_routing.md
@@ -134,4 +134,47 @@ Examples:
}
}
```
-Each request header entry is validated in the format `: `, so you are able to filter for request header names and values.
\ No newline at end of file
+Each request header entry is validated in the format `: `, so you are able to filter for request header names and values.
+
+## Micrometer metrics
+The routing feature is monitored with micrometer. The following metrics are available:
+* gateleen_forwarded_total
+* gateleen_forwarded_seconds
+* gateleen_forwarded_seconds_max
+* gateleen_forwarded_seconds_count
+* gateleen_forwarded_seconds_sum
+
+Additional tags are provided to split the forward count into sub counts.
+
+| tag | description |
+|------------|-------------------------------------------------------------------------------------------------------------------|
+| metricName | The `metricName` property from the corresponding routing rule. With this, you are able to count requests per rule |
+| type | Describes where the request was forwarded to. Possible values are `local`, `external` and `null` |
+| quantile | Values of `0.75` and `0.95` for percentile durations of requests |
+
+
+Example metrics:
+
+```
+# HELP gateleen_forwarded_total Amount of forwarded requests
+# TYPE gateleen_forwarded_total counter
+gateleen_forwarded_total{metricName="storage-resources",type="storage",} 67565.0
+gateleen_forwarded_total{metricName="infotool_v1_informations",type="external",} 655.0
+gateleen_forwarded_total{metricName="infotool-v1",type="storage",} 4320.0
+# HELP gateleen_forwarded_seconds_max Durations of forwarded requests
+# TYPE gateleen_forwarded_seconds_max gauge
+gateleen_forwarded_seconds_max{metricName="storage-resources",type="storage",} 8.5515
+gateleen_forwarded_seconds_max{metricName="infotool_v1_informations",type="external",} 3.456
+# HELP gateleen_forwarded_seconds Durations of forwarded requests
+# TYPE gateleen_forwarded_seconds summary
+gateleen_forwarded_seconds{metricName="storage-resources",type="storage",quantile="0.75",} 6.2158
+gateleen_forwarded_seconds{metricName="storage-resources",type="storage",quantile="0.95",} 8.2123
+gateleen_forwarded_seconds_count{metricName="storage-resources",type="storage",} 67565.0
+gateleen_forwarded_seconds_sum{metricName="storage-resources",type="storage",} 656434.0
+gateleen_forwarded_seconds{metricName="infotool_v1_informations",type="external",quantile="0.75",} 4.2365
+gateleen_forwarded_seconds{metricName="infotool_v1_informations",type="external",quantile="0.95",} 4.8998
+gateleen_forwarded_seconds_count{metricName="infotool_v1_informations",type="external",} 7567.0
+gateleen_forwarded_seconds_sum{metricName="infotool_v1_informations",type="external",} 256324.0
+```
+
+To enable the metrics, set a `MeterRegistry` instance by calling `withMeterRegistry(MeterRegistry meterRegistry)` method in `RouterBuilder` class.
\ No newline at end of file
diff --git a/gateleen-routing/pom.xml b/gateleen-routing/pom.xml
index 26f4bfceb..91cfa062c 100644
--- a/gateleen-routing/pom.xml
+++ b/gateleen-routing/pom.xml
@@ -27,6 +27,10 @@
gateleen-monitoring
${project.version}
+
+ io.micrometer
+ micrometer-core
+
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/AbstractForwarder.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/AbstractForwarder.java
index 7cab72a0e..dccf549bb 100644
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/AbstractForwarder.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/AbstractForwarder.java
@@ -1,5 +1,8 @@
package org.swisspush.gateleen.routing;
+import javax.annotation.Nullable;
+
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.ext.web.RoutingContext;
@@ -18,21 +21,34 @@ public abstract class AbstractForwarder implements Handler {
protected final LoggingResourceManager loggingResourceManager;
protected final LogAppenderRepository logAppenderRepository;
protected final MonitoringHandler monitoringHandler;
+ protected final String metricNameTag;
+
+ public static final String FORWARDER_COUNT_METRIC_NAME = "gateleen.forwarded";
+ public static final String FORWARDER_COUNT_METRIC_DESCRIPTION = "Amount of forwarded requests";
+ public static final String FORWARDS_METRIC_NAME = "gateleen.forwarded.seconds";
+ public static final String FORWARDS_METRIC_DESCRIPTION = "Durations of forwarded requests";
+ public static final String FORWARDER_METRIC_TAG_TYPE = "type";
+ public static final String FORWARDER_METRIC_TAG_METRICNAME = "metricName";
+ public static final String FORWARDER_NO_METRICNAME = "no-metric-name";
- public AbstractForwarder(Rule rule, LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler) {
+ public AbstractForwarder(Rule rule, LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler) {
this.rule = rule;
this.loggingResourceManager = loggingResourceManager;
this.logAppenderRepository = logAppenderRepository;
this.monitoringHandler = monitoringHandler;
+
+ this.metricNameTag = rule.getMetricName() != null ? rule.getMetricName() : FORWARDER_NO_METRICNAME;
}
+ protected abstract void setMeterRegistry(MeterRegistry meterRegistry);
+
protected boolean doHeadersFilterMatch(final HttpServerRequest request) {
final Logger log = RequestLoggerFactory.getLogger(getClass(), request);
- if(rule.getHeadersFilterPattern() != null){
+ if (rule.getHeadersFilterPattern() != null) {
log.debug("Looking for request headers with pattern {}", rule.getHeadersFilterPattern().pattern());
boolean matchFound = HttpHeaderUtil.hasMatchingHeader(request.headers(), rule.getHeadersFilterPattern());
- if(matchFound) {
+ if (matchFound) {
log.debug("Matching request headers found");
} else {
log.debug("No matching request headers found. Looking for the next routing rule");
@@ -64,4 +80,11 @@ protected void respondError(HttpServerRequest req, StatusCode statusCode) {
log.warn("IllegalStateException while sending error response for {}", req.uri(), ex);
}
}
+
+ protected String getRequestTarget(String target) {
+ if (target != null && (target.contains("localhost") || target.contains("127.0.0.1"))) {
+ return "local";
+ }
+ return "external";
+ }
}
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Forwarder.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Forwarder.java
index 3a049add7..9eb2a7189 100755
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Forwarder.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Forwarder.java
@@ -1,5 +1,8 @@
package org.swisspush.gateleen.routing;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
import io.netty.channel.ConnectTimeoutException;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.*;
@@ -64,10 +67,13 @@ public class Forwarder extends AbstractForwarder {
private static final int STATUS_CODE_2XX = 2;
private static final Logger LOG = LoggerFactory.getLogger(Forwarder.class);
+ private Counter forwardCounter;
+ private Timer forwardTimer;
+ private MeterRegistry meterRegistry;
public Forwarder(Vertx vertx, HttpClient client, Rule rule, final ResourceStorage storage,
- LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
- String userProfilePath, @Nullable AuthStrategy authStrategy) {
+ LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository,
+ @Nullable MonitoringHandler monitoringHandler, String userProfilePath, @Nullable AuthStrategy authStrategy) {
super(rule, loggingResourceManager, logAppenderRepository, monitoringHandler);
this.vertx = vertx;
this.client = client;
@@ -82,6 +88,31 @@ public Forwarder(Vertx vertx, HttpClient client, Rule rule, final ResourceStorag
this.authStrategy = authStrategy;
}
+ /**
+ * Sets the MeterRegistry for this Forwarder.
+ * If the provided MeterRegistry is not null, it initializes the forwardCounter
+ * with the appropriate metric name, description, and tags.
+ *
+ * @param meterRegistry the MeterRegistry to set
+ */
+ @Override
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ if (meterRegistry != null) {
+ forwardCounter = Counter.builder(FORWARDER_COUNT_METRIC_NAME)
+ .description(FORWARDER_COUNT_METRIC_DESCRIPTION)
+ .tag(FORWARDER_METRIC_TAG_TYPE, getRequestTarget(target))
+ .tag(FORWARDER_METRIC_TAG_METRICNAME, metricNameTag)
+ .register(meterRegistry);
+ forwardTimer = Timer.builder(FORWARDS_METRIC_NAME)
+ .description(FORWARDS_METRIC_DESCRIPTION)
+ .publishPercentiles(0.75, 0.95)
+ .tag(FORWARDER_METRIC_TAG_METRICNAME, metricNameTag)
+ .tag(FORWARDER_METRIC_TAG_TYPE, getRequestTarget(target))
+ .register(meterRegistry);
+ }
+ }
+
private Map createProfileHeaderValues(JsonObject profile, Logger log) {
Map profileValues = new HashMap<>();
if (rule.getProfile() != null) {
@@ -148,15 +179,22 @@ public void handle(final RoutingContext ctx, final Buffer bodyData, @Nullable fi
port = rule.getPort();
}
target = rule.getHost() + ":" + port;
- monitoringHandler.updateRequestsMeter(target, req.uri());
- monitoringHandler.updateRequestPerRuleMonitoring(req, rule.getMetricName());
+
+ if (forwardCounter != null) {
+ forwardCounter.increment();
+ }
+
+ if (monitoringHandler != null) {
+ monitoringHandler.updateRequestsMeter(target, req.uri());
+ monitoringHandler.updateRequestPerRuleMonitoring(req, rule.getMetricName());
+ }
final String targetUri = urlPattern.matcher(req.uri()).replaceFirst(rule.getPath()).replaceAll("\\/\\/", "/");
log.debug("Forwarding request: {} to {}://{} with rule {}", req.uri(), rule.getScheme(), target + targetUri, rule.getRuleIdentifier());
final String userId = extractUserId(req, log);
req.pause(); // pause the request to avoid problems with starting another async request (storage)
maybeAuthenticate(rule).onComplete(event -> {
- if(event.failed()) {
+ if (event.failed()) {
req.resume();
log.error("Failed to authenticate request. Cause: {}", event.cause().getMessage());
respondError(req, StatusCode.UNAUTHORIZED);
@@ -184,6 +222,12 @@ public void handle(final RoutingContext ctx, final Buffer bodyData, @Nullable fi
});
}
+ private void handleForwardDurationMetrics(Timer.Sample timerSample) {
+ if (timerSample != null && forwardTimer != null) {
+ timerSample.stop(forwardTimer);
+ }
+ }
+
/**
* Returns the userId defined in the on-behalf-of-header if provided, the userId from user-header otherwise.
*
@@ -240,7 +284,19 @@ private void handleRequest(final HttpServerRequest req, final Buffer bodyData, f
final String uniqueId = req.headers().get("x-rp-unique_id");
final String timeout = req.headers().get("x-timeout");
- final long startTime = monitoringHandler.startRequestMetricTracking(rule.getMetricName(), req.uri());
+ Long startTime = null;
+
+ Timer.Sample timerSample = null;
+ if (meterRegistry != null) {
+ timerSample = Timer.start(meterRegistry);
+ }
+
+ if (monitoringHandler != null) {
+ startTime = monitoringHandler.startRequestMetricTracking(rule.getMetricName(), req.uri());
+ }
+
+ Long finalStartTime = startTime;
+ Timer.Sample finalTimerSample = timerSample;
client.request(req.method(), port, rule.getHost(), targetUri, new Handler<>() {
@Override
@@ -256,7 +312,7 @@ public void handle(AsyncResult event) {
return;
}
HttpClientRequest cReq = event.result();
- final Handler> cResHandler = getAsyncHttpClientResponseHandler(req, targetUri, log, profileHeaderMap, loggingHandler, startTime, afterHandler);
+ final Handler> cResHandler = getAsyncHttpClientResponseHandler(req, targetUri, log, profileHeaderMap, loggingHandler, finalStartTime, finalTimerSample, afterHandler);
cReq.response(cResHandler);
if (timeout != null) {
@@ -290,7 +346,7 @@ public void handle(AsyncResult event) {
return;
}
- installExceptionHandler(req, targetUri, startTime, cReq);
+ installExceptionHandler(req, targetUri, finalStartTime, finalTimerSample, cReq);
/*
* If no bodyData is available
@@ -421,9 +477,14 @@ private void setProfileHeaders(Logger log, Map profileHeaderMap,
}
}
- private void installExceptionHandler(final HttpServerRequest req, final String targetUri, final long startTime, HttpClientRequest cReq) {
+ private void installExceptionHandler(final HttpServerRequest req, final String targetUri, final Long startTime, @Nullable Timer.Sample timerSample, HttpClientRequest cReq) {
cReq.exceptionHandler(exception -> {
- monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), startTime, req.uri());
+ if (monitoringHandler != null && startTime != null) {
+ monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), startTime, req.uri());
+ }
+
+ handleForwardDurationMetrics(timerSample);
+
if (exception instanceof TimeoutException) {
error("Timeout", req, targetUri);
respondError(req, StatusCode.TIMEOUT);
@@ -445,7 +506,7 @@ private void installExceptionHandler(final HttpServerRequest req, final String t
});
}
- private Handler> getAsyncHttpClientResponseHandler(final HttpServerRequest req, final String targetUri, final Logger log, final Map profileHeaderMap, final LoggingHandler loggingHandler, final long startTime, @Nullable final Handler afterHandler) {
+ private Handler> getAsyncHttpClientResponseHandler(final HttpServerRequest req, final String targetUri, final Logger log, final Map profileHeaderMap, final LoggingHandler loggingHandler, @Nullable final Long startTime, @Nullable Timer.Sample timerSample, @Nullable final Handler afterHandler) {
return asyncResult -> {
HttpClientResponse cRes = asyncResult.result();
if (asyncResult.failed()) {
@@ -462,7 +523,12 @@ private Handler> getAsyncHttpClientResponseHandl
}
return;
}
- monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), startTime, req.uri());
+ if (monitoringHandler != null) {
+ monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), startTime, req.uri());
+ }
+
+ handleForwardDurationMetrics(timerSample);
+
loggingHandler.setResponse(cRes);
req.response().setStatusCode(cRes.statusCode());
req.response().setStatusMessage(cRes.statusMessage());
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/NullForwarder.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/NullForwarder.java
index 8d1b8fb48..bed0d498a 100755
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/NullForwarder.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/NullForwarder.java
@@ -1,5 +1,7 @@
package org.swisspush.gateleen.routing;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.MultiMap;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
@@ -17,6 +19,8 @@
import org.swisspush.gateleen.logging.LoggingResourceManager;
import org.swisspush.gateleen.monitoring.MonitoringHandler;
+import javax.annotation.Nullable;
+
/**
* Consumes requests without forwarding them anywhere.
*
@@ -25,12 +29,31 @@
public class NullForwarder extends AbstractForwarder {
private EventBus eventBus;
+ private Counter forwardCounter;
- public NullForwarder(Rule rule, LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler, EventBus eventBus) {
+ public NullForwarder(Rule rule, LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler, EventBus eventBus) {
super(rule, loggingResourceManager, logAppenderRepository, monitoringHandler);
this.eventBus = eventBus;
}
+ /**
+ * Sets the MeterRegistry for this NullForwarder.
+ * If the provided MeterRegistry is not null, it initializes the forwardCounter
+ * with the appropriate metric name, description, and tags.
+ *
+ * @param meterRegistry the MeterRegistry to set
+ */
+ @Override
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ if(meterRegistry != null) {
+ forwardCounter = Counter.builder(FORWARDER_COUNT_METRIC_NAME)
+ .description(FORWARDER_COUNT_METRIC_DESCRIPTION)
+ .tag(FORWARDER_METRIC_TAG_METRICNAME, metricNameTag)
+ .tag(FORWARDER_METRIC_TAG_TYPE, "null")
+ .register(meterRegistry);
+ }
+ }
+
@Override
public void handle(final RoutingContext ctx) {
final Logger log = RequestLoggerFactory.getLogger(NullForwarder.class, ctx.request());
@@ -40,7 +63,13 @@ public void handle(final RoutingContext ctx) {
return;
}
- monitoringHandler.updateRequestPerRuleMonitoring(ctx.request(), rule.getMetricName());
+ if(forwardCounter != null) {
+ forwardCounter.increment();
+ }
+
+ if(monitoringHandler != null) {
+ monitoringHandler.updateRequestPerRuleMonitoring(ctx.request(), rule.getMetricName());
+ }
final LoggingHandler loggingHandler = new LoggingHandler(loggingResourceManager, logAppenderRepository, ctx.request(), eventBus);
log.debug("Not forwarding request: {} with rule {}", ctx.request().uri(), rule.getRuleIdentifier());
final HeadersMultiMap requestHeaders = new HeadersMultiMap();
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Router.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Router.java
index 7c430f2e5..80add04a2 100755
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Router.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/Router.java
@@ -1,5 +1,6 @@
package org.swisspush.gateleen.routing;
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
@@ -62,6 +63,7 @@ public class Router implements Refreshable, LoggableResource, ConfigurationResou
private final LoggingResourceManager loggingResourceManager;
private final LogAppenderRepository logAppenderRepository;
private final MonitoringHandler monitoringHandler;
+ private final MeterRegistry meterRegistry;
private final Logger log = LoggerFactory.getLogger(Router.class);
private final Logger cleanupLogger = LoggerFactory.getLogger(Router.class.getName() + "Cleanup");
private final Vertx vertx;
@@ -108,7 +110,8 @@ public static RouterBuilder builder() {
final Map properties,
LoggingResourceManager loggingResourceManager,
LogAppenderRepository logAppenderRepository,
- MonitoringHandler monitoringHandler,
+ @Nullable MonitoringHandler monitoringHandler,
+ MeterRegistry meterRegistry,
HttpClient selfClient,
String serverPath,
String rulesPath,
@@ -126,6 +129,7 @@ public static RouterBuilder builder() {
this.loggingResourceManager = loggingResourceManager;
this.logAppenderRepository = logAppenderRepository;
this.monitoringHandler = monitoringHandler;
+ this.meterRegistry = meterRegistry;
this.selfClient = selfClient;
this.vertx = vertx;
this.sharedData = vertx.sharedData().getLocalMap(ROUTER_STATE_MAP);
@@ -322,7 +326,7 @@ private void createForwarders(List rules, io.vertx.ext.web.Router newRoute
* is null.
*/
AuthStrategy authStrategy = selectAuthStrategy(rule);
- Handler forwarder;
+ AbstractForwarder forwarder;
if (rule.getPath() == null) {
forwarder = new NullForwarder(rule, loggingResourceManager, logAppenderRepository, monitoringHandler,
vertx.eventBus());
@@ -339,6 +343,8 @@ private void createForwarders(List rules, io.vertx.ext.web.Router newRoute
newClients.add(client);
}
+ forwarder.setMeterRegistry(meterRegistry);
+
if (rule.getMethods() == null) {
log.info("Installing {} forwarder for all methods: {}", rule.getScheme().toUpperCase(), rule.getUrlPattern());
newRouter.routeWithRegex(rule.getUrlPattern()).handler(forwarder);
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/RouterBuilder.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/RouterBuilder.java
index f7ef9685f..59e290ad8 100644
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/RouterBuilder.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/RouterBuilder.java
@@ -1,5 +1,6 @@
package org.swisspush.gateleen.routing;
+import io.micrometer.core.instrument.MeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpClient;
@@ -37,6 +38,7 @@ public class RouterBuilder {
private LoggingResourceManager loggingResourceManager;
private LogAppenderRepository logAppenderRepository;
private MonitoringHandler monitoringHandler;
+ private MeterRegistry meterRegistry;
private HttpClient selfClient;
private String serverPath;
private String rulesPath;
@@ -98,6 +100,7 @@ public Router build() {
loggingResourceManager,
logAppenderRepository,
monitoringHandler,
+ meterRegistry,
selfClient,
serverPath,
rulesPath,
@@ -183,6 +186,12 @@ public RouterBuilder withMonitoringHandler(MonitoringHandler monitoringHandler)
return this;
}
+ public RouterBuilder withMeterRegistry(MeterRegistry meterRegistry) {
+ ensureNotBuilt();
+ this.meterRegistry = meterRegistry;
+ return this;
+ }
+
public RouterBuilder withSelfClient(HttpClient selfClient) {
ensureNotBuilt();
this.selfClient = selfClient;
diff --git a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/StorageForwarder.java b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/StorageForwarder.java
index 1e48c2212..32a0f38a1 100755
--- a/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/StorageForwarder.java
+++ b/gateleen-routing/src/main/java/org/swisspush/gateleen/routing/StorageForwarder.java
@@ -1,5 +1,8 @@
package org.swisspush.gateleen.routing;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Timer;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
@@ -28,6 +31,7 @@
import org.swisspush.gateleen.logging.LoggingResourceManager;
import org.swisspush.gateleen.monitoring.MonitoringHandler;
+import javax.annotation.Nullable;
import java.util.regex.Pattern;
/**
@@ -43,8 +47,14 @@ public class StorageForwarder extends AbstractForwarder {
private CORSHandler corsHandler;
private GateleenExceptionFactory gateleenExceptionFactory;
+ private Counter forwardCounter;
+ private Timer forwardTimer;
+ private MeterRegistry meterRegistry;
+
+ private static final String TYPE_STORAGE = "storage";
+
public StorageForwarder(EventBus eventBus, Rule rule, LoggingResourceManager loggingResourceManager,
- LogAppenderRepository logAppenderRepository, MonitoringHandler monitoringHandler,
+ LogAppenderRepository logAppenderRepository, @Nullable MonitoringHandler monitoringHandler,
GateleenExceptionFactory gateleenExceptionFactory) {
super(rule, loggingResourceManager, logAppenderRepository, monitoringHandler);
this.eventBus = eventBus;
@@ -54,6 +64,32 @@ public StorageForwarder(EventBus eventBus, Rule rule, LoggingResourceManager log
this.gateleenExceptionFactory = gateleenExceptionFactory;
}
+ /**
+ * Sets the MeterRegistry for this StorageForwarder.
+ * If the provided MeterRegistry is not null, it initializes the forwardCounter
+ * with the appropriate metric name, description, and tags.
+ *
+ * @param meterRegistry the MeterRegistry to set
+ */
+ @Override
+ public void setMeterRegistry(MeterRegistry meterRegistry) {
+ this.meterRegistry = meterRegistry;
+ if (meterRegistry != null) {
+ forwardCounter = Counter.builder(FORWARDER_COUNT_METRIC_NAME)
+ .description(FORWARDER_COUNT_METRIC_DESCRIPTION)
+ .tag(FORWARDER_METRIC_TAG_METRICNAME, metricNameTag)
+ .tag(FORWARDER_METRIC_TAG_TYPE, TYPE_STORAGE)
+ .register(meterRegistry);
+
+ forwardTimer = Timer.builder(FORWARDS_METRIC_NAME)
+ .description(FORWARDS_METRIC_DESCRIPTION)
+ .publishPercentiles(0.75, 0.95)
+ .tag(FORWARDER_METRIC_TAG_METRICNAME, metricNameTag)
+ .tag(FORWARDER_METRIC_TAG_TYPE, TYPE_STORAGE)
+ .register(meterRegistry);
+ }
+ }
+
@Override
public void handle(final RoutingContext ctx) {
final LoggingHandler loggingHandler = new LoggingHandler(loggingResourceManager, logAppenderRepository, ctx.request(), this.eventBus);
@@ -65,9 +101,19 @@ public void handle(final RoutingContext ctx) {
return;
}
- monitoringHandler.updateRequestsMeter("localhost", ctx.request().uri());
- monitoringHandler.updateRequestPerRuleMonitoring(ctx.request(), rule.getMetricName());
- final long startTime = monitoringHandler.startRequestMetricTracking(rule.getMetricName(), ctx.request().uri());
+ Long startTime = null;
+
+ Timer.Sample timerSample = null;
+ if(meterRegistry != null) {
+ timerSample = Timer.start(meterRegistry);
+ forwardCounter.increment();
+ }
+
+ if (monitoringHandler != null) {
+ monitoringHandler.updateRequestsMeter("localhost", ctx.request().uri());
+ monitoringHandler.updateRequestPerRuleMonitoring(ctx.request(), rule.getMetricName());
+ startTime = monitoringHandler.startRequestMetricTracking(rule.getMetricName(), ctx.request().uri());
+ }
log.debug("Forwarding {} request: {} to storage {} {} with rule {}", ctx.request().method(), ctx.request().uri(),
rule.getStorage(), targetUri, rule.getRuleIdentifier());
final HeadersMultiMap requestHeaders = new HeadersMultiMap();
@@ -92,68 +138,79 @@ public void handle(final RoutingContext ctx) {
loggingHandler.appendRequestPayload(buffer, requestHeaders);
requestBuffer.appendBuffer(buffer);
});
+ Long finalStartTime = startTime;
+
+ Timer.Sample finalTimerSample = timerSample;
+
ctx.request().endHandler(event ->
eventBus.request(address, requestBuffer, new DeliveryOptions().setSendTimeout(10000),
(Handler>>) result -> {
- HttpServerResponse response = ctx.response();
- monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), startTime, ctx.request().uri());
- if (result.failed()) {
- String statusMessage = "Storage request for " + ctx.request().uri() + " failed with message: " + result.cause().getMessage();
- response.setStatusCode(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
- response.setStatusMessage(statusMessage);
- response.end();
- log.error("{}", statusMessage, gateleenExceptionFactory.newException(result.cause()));
- } else {
- Buffer buffer = result.result().body();
- int headerLength = buffer.getInt(0);
- JsonObject responseJson = new JsonObject(buffer.getString(4, headerLength + 4));
- JsonArray headers = responseJson.getJsonArray("headers");
- MultiMap responseHeaders = null;
- if (headers != null && !headers.isEmpty()) {
- responseHeaders = JsonMultiMap.fromJson(headers);
-
- setUniqueIdHeader(responseHeaders);
-
- ctx.response().headers().setAll(responseHeaders);
- }
- corsHandler.handle(ctx.request());
- int statusCode = responseJson.getInteger("statusCode");
-
- // translate with header info
- int translatedStatus = Translator.translateStatusCode(statusCode, ctx.request().headers());
-
- // nothing changed?
- if (statusCode == translatedStatus) {
- translatedStatus = Translator.translateStatusCode(statusCode, rule, log);
- }
-
- boolean translated = statusCode != translatedStatus;
-
- // set the statusCode (if nothing hapend, it will remain the same)
- statusCode = translatedStatus;
-
- response.setStatusCode(statusCode);
- String statusMessage;
- if (translated) {
- statusMessage = HttpResponseStatus.valueOf(statusCode).reasonPhrase();
- response.setStatusMessage(statusMessage);
- } else {
- statusMessage = responseJson.getString("statusMessage");
- if (statusMessage != null) {
- response.setStatusMessage(statusMessage);
- }
- }
- Buffer data = buffer.getBuffer(4 + headerLength, buffer.length());
- response.headers().set("content-length", "" + data.length());
- response.write(data);
- response.end();
- ResponseStatusCodeLogUtil.debug(ctx.request(), StatusCode.fromCode(statusCode), StorageForwarder.class);
- if (responseHeaders != null) {
- loggingHandler.appendResponsePayload(data, responseHeaders);
- }
- loggingHandler.log(ctx.request().uri(), ctx.request().method(), statusCode, statusMessage,
- requestHeaders, responseHeaders != null ? responseHeaders : new HeadersMultiMap());
- }
+ HttpServerResponse response = ctx.response();
+ if (monitoringHandler != null) {
+ monitoringHandler.stopRequestMetricTracking(rule.getMetricName(), finalStartTime, ctx.request().uri());
+ }
+
+ if(finalTimerSample != null) {
+ finalTimerSample.stop(forwardTimer);
+ }
+
+ if (result.failed()) {
+ String statusMessage = "Storage request for " + ctx.request().uri() + " failed with message: " + result.cause().getMessage();
+ response.setStatusCode(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode());
+ response.setStatusMessage(statusMessage);
+ response.end();
+ log.error("{}", statusMessage, gateleenExceptionFactory.newException(result.cause()));
+ } else {
+ Buffer buffer = result.result().body();
+ int headerLength = buffer.getInt(0);
+ JsonObject responseJson = new JsonObject(buffer.getString(4, headerLength + 4));
+ JsonArray headers = responseJson.getJsonArray("headers");
+ MultiMap responseHeaders = null;
+ if (headers != null && !headers.isEmpty()) {
+ responseHeaders = JsonMultiMap.fromJson(headers);
+
+ setUniqueIdHeader(responseHeaders);
+
+ ctx.response().headers().setAll(responseHeaders);
+ }
+ corsHandler.handle(ctx.request());
+ int statusCode = responseJson.getInteger("statusCode");
+
+ // translate with header info
+ int translatedStatus = Translator.translateStatusCode(statusCode, ctx.request().headers());
+
+ // nothing changed?
+ if (statusCode == translatedStatus) {
+ translatedStatus = Translator.translateStatusCode(statusCode, rule, log);
+ }
+
+ boolean translated = statusCode != translatedStatus;
+
+ // set the statusCode (if nothing hapend, it will remain the same)
+ statusCode = translatedStatus;
+
+ response.setStatusCode(statusCode);
+ String statusMessage;
+ if (translated) {
+ statusMessage = HttpResponseStatus.valueOf(statusCode).reasonPhrase();
+ response.setStatusMessage(statusMessage);
+ } else {
+ statusMessage = responseJson.getString("statusMessage");
+ if (statusMessage != null) {
+ response.setStatusMessage(statusMessage);
+ }
+ }
+ Buffer data = buffer.getBuffer(4 + headerLength, buffer.length());
+ response.headers().set("content-length", "" + data.length());
+ response.write(data);
+ response.end();
+ ResponseStatusCodeLogUtil.debug(ctx.request(), StatusCode.fromCode(statusCode), StorageForwarder.class);
+ if (responseHeaders != null) {
+ loggingHandler.appendResponsePayload(data, responseHeaders);
+ }
+ loggingHandler.log(ctx.request().uri(), ctx.request().method(), statusCode, statusMessage,
+ requestHeaders, responseHeaders != null ? responseHeaders : new HeadersMultiMap());
+ }
}));
}
diff --git a/gateleen-routing/src/test/java/org/swisspush/gateleen/routing/RouterTest.java b/gateleen-routing/src/test/java/org/swisspush/gateleen/routing/RouterTest.java
index b1184d662..2c90dfbc2 100755
--- a/gateleen-routing/src/test/java/org/swisspush/gateleen/routing/RouterTest.java
+++ b/gateleen-routing/src/test/java/org/swisspush/gateleen/routing/RouterTest.java
@@ -1,6 +1,9 @@
package org.swisspush.gateleen.routing;
import com.google.common.collect.ImmutableMap;
+import io.micrometer.core.instrument.Counter;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
@@ -28,10 +31,7 @@
import org.swisspush.gateleen.logging.LoggingResourceManager;
import org.swisspush.gateleen.monitoring.MonitoringHandler;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
/**
* Tests for the Router class
@@ -45,6 +45,7 @@ public class RouterTest {
private Map properties;
private LoggingResourceManager loggingResourceManager;
private MonitoringHandler monitoringHandler;
+ private MeterRegistry meterRegistry;
private HttpClient httpClient;
private String serverUrl;
private String rulesPath;
@@ -143,6 +144,7 @@ public void setUp() {
Mockito.when(loggingResourceManager.getLoggingResource()).thenReturn(new LoggingResource());
monitoringHandler = Mockito.mock(MonitoringHandler.class);
httpClient = Mockito.mock(HttpClient.class);
+ meterRegistry = new SimpleMeterRegistry();
serverUrl = "/gateleen/server";
rulesPath = serverUrl + "/admin/v1/routing/rules";
@@ -160,6 +162,7 @@ private RouterBuilder routerBuilder() {
.withProperties(properties)
.withLoggingResourceManager(loggingResourceManager)
.withMonitoringHandler(monitoringHandler)
+ .withMeterRegistry(meterRegistry)
.withSelfClient(httpClient)
.withServerPath(serverUrl)
.withRulesPath(rulesPath)
@@ -168,6 +171,12 @@ private RouterBuilder routerBuilder() {
.withStoragePort(storagePort);
}
+ private void assertNoCountersIncremented(TestContext context) {
+ for (Counter counter : meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).counters()) {
+ context.assertEquals(0.0, counter.count(), "No counter should have been incremented");
+ }
+ }
+
@Test
public void testRequestHopValidationLimitNotYetReached(TestContext context) {
storage = new MockResourceStorage(ImmutableMap.of(rulesPath, RULES_WITH_HOPS, serverUrl + "/loop/4/resource", RANDOM_RESOURCE));
@@ -240,6 +249,9 @@ public DummyHttpServerResponse response() {
GETRandomResourceRequest request = new GETRandomResourceRequest();
router.route(request);
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "loop_4").counter();
+ context.assertEquals(1.0, counter.count(), "Counter for `loop_4` rule should have been incremented by 1");
+
context.assertEquals("1", request.headers().get("x-hops"), "x-hops header should have value 1");
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
@@ -317,6 +329,8 @@ public DummyHttpServerResponse response() {
GETRandomResourceRequest request = new GETRandomResourceRequest();
router.route(request);
+ assertNoCountersIncremented(context);
+
context.assertEquals("1", request.headers().get("x-hops"), "x-hops header should have value 1");
context.assertEquals(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 500");
context.assertEquals("Request hops limit exceeded", request.response().getStatusMessage(), "StatusMessage should be 'Request hops limit exceeded'");
@@ -404,6 +418,9 @@ public DummyHttpServerResponse response() {
context.assertEquals("6", request.headers().get("x-hops"), "x-hops header should have value 6");
context.assertEquals(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 500");
context.assertEquals("Request hops limit exceeded", request.response().getStatusMessage(), "StatusMessage should be 'Request hops limit exceeded'");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "loop_4").counter();
+ context.assertEquals(5.0, counter.count(), "Counter for `loop_4` rule should have been incremented by 5");
}
@Test
@@ -478,6 +495,9 @@ public DummyHttpServerResponse response() {
context.assertNull(request.headers().get("x-hops"), "No x-hops header should be present");
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "loop_4").counter();
+ context.assertEquals(20.0, counter.count(), "Counter for `loop_4` rule should have been incremented by 20");
}
@@ -488,6 +508,8 @@ public void testRouterConstructionValidConfiguration(TestContext context) {
Router router = routerBuilder().withProperties(properties).build();
context.assertFalse(router.isRoutingBroken(), "Routing should not be broken");
context.assertNull(router.getRoutingBrokenMessage(), "RoutingBrokenMessage should be null");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -496,6 +518,8 @@ public void testRouterConstructionWithMissingProperty(TestContext context) {
context.assertTrue(router.isRoutingBroken(), "Routing should be broken because of missing properties entry");
context.assertNotNull(router.getRoutingBrokenMessage(), "RoutingBrokenMessage should contain 'gateleen.test.prop.1' property");
context.assertTrue(router.getRoutingBrokenMessage().contains("gateleen.test.prop.1"), "RoutingBrokenMessage should contain 'gateleen.test.prop.1' property");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -538,6 +562,8 @@ public HttpServerResponse response() {
router.route(new UpdateRulesWithValidResourceRequest());
context.assertFalse(router.isRoutingBroken(), "Routing should not be broken anymore");
context.assertNull(router.getRoutingBrokenMessage(), "RoutingBrokenMessage should be null");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -578,6 +604,8 @@ public MultiMap headers() {
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
context.assertEquals(RULES_WITH_MISSING_PROPS, request.response().getResultBuffer(), "RoutingRules should be returned as result");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -619,6 +647,8 @@ public MultiMap headers() {
context.assertEquals(StatusCode.INTERNAL_SERVER_ERROR.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be Internal Server Error");
context.assertTrue(request.response().getResultBuffer().contains("Routing is broken"), "Routing is broken message should be returned");
context.assertTrue(request.response().getResultBuffer().contains("gateleen.test.prop.1"), "The message should contain 'gateleen.test.prop.1' in the message");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -742,6 +772,9 @@ public HttpServerRequest pause() {
router.route(requestRandomResource);
context.assertFalse(router.isRoutingBroken(), "Routing should not be broken anymore");
context.assertNull(router.getRoutingBrokenMessage(), "RoutingBrokenMessage should be null");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_TYPE, "local").counter();
+ context.assertEquals(1.0, counter.count(), "Counter should have been incremented by 1");
}
@Test
@@ -793,6 +826,8 @@ public DummyHttpServerResponse response() {
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
context.assertEquals(ts, new JsonObject(request.response().getResultBuffer()).getLong("ts"));
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -848,6 +883,7 @@ public DummyHttpServerResponse response() {
router.route(request);
context.assertEquals(StatusCode.NOT_FOUND.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 404");
+ assertNoCountersIncremented(context);
}
@Test
@@ -871,6 +907,9 @@ public void testStorageRequestWithHeadersFilterPresent(TestContext context) {
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "forward_storage").counter();
+ context.assertEquals(1.0, counter.count(), "Counter for `forward_storage` rule should have been incremented by 1");
}
@Test
@@ -892,6 +931,8 @@ public void testStorageRequestWithHeadersFilterAbsent(TestContext context) {
router.route(request);
context.assertEquals(StatusCode.NOT_FOUND.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 404");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -914,6 +955,8 @@ public void testNullForwarderRequestWithHeadersFilterNotMatching(TestContext con
router.route(request);
context.assertEquals(StatusCode.NOT_FOUND.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 404");
+
+ assertNoCountersIncremented(context);
}
@Test
@@ -938,6 +981,9 @@ public void testNullForwarderRequestWithHeadersFilterPresent(TestContext context
context.assertEquals(StatusCode.OK.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 200");
context.assertEquals(StatusCode.OK.getStatusMessage(), request.response().getStatusMessage(), "StatusMessage should be OK");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "forward_null").counter();
+ context.assertEquals(1.0, counter.count(), "Counter for `forward_null` rule should have been incremented by 1");
}
@Test
@@ -963,6 +1009,9 @@ public void testForwarderRequestWithHeadersFilterPresent(TestContext context) {
// we expect a status code 500 because of a NullPointerException in the test setup
// however, this means that the headersFilter evaluation did not return a 400 Bad Request
context.assertEquals(StatusCode.INTERNAL_SERVER_ERROR.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 500");
+
+ Counter counter = meterRegistry.get(AbstractForwarder.FORWARDER_COUNT_METRIC_NAME).tag(AbstractForwarder.FORWARDER_METRIC_TAG_METRICNAME, "forward_backend").counter();
+ context.assertEquals(1.0, counter.count(), "Counter for `forward_backend` rule should have been incremented by 1");
}
@Test
@@ -986,6 +1035,8 @@ public void testForwarderRequestWithHeadersFilterNotMatching(TestContext context
router.route(request);
context.assertEquals(StatusCode.NOT_FOUND.getStatusCode(), request.response().getStatusCode(), "StatusCode should be 404");
+
+ assertNoCountersIncremented(context);
}
private DummyHttpServerRequest buildRequest(HttpMethod method, String uri, MultiMap headers, Buffer body, DummyHttpServerResponse response) {
diff --git a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/Scheduler.java b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/Scheduler.java
index 65ab5a309..6577b6946 100755
--- a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/Scheduler.java
+++ b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/Scheduler.java
@@ -15,6 +15,7 @@
import org.swisspush.gateleen.queue.expiry.ExpiryCheckHandler;
import org.swisspush.gateleen.queue.queuing.QueueClient;
+import javax.annotation.Nullable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
@@ -48,17 +49,17 @@ public class Scheduler {
private Logger log;
public Scheduler(
- Vertx vertx,
- String redisquesAddress,
- RedisProvider redisProvider,
- GateleenExceptionFactory exceptionFactory,
- String name,
- String cronExpression,
- List requests,
- MonitoringHandler monitoringHandler,
- int maxRandomOffset,
- boolean executeOnStartup,
- boolean executeOnReload
+ Vertx vertx,
+ String redisquesAddress,
+ RedisProvider redisProvider,
+ GateleenExceptionFactory exceptionFactory,
+ String name,
+ String cronExpression,
+ List requests,
+ @Nullable MonitoringHandler monitoringHandler,
+ int maxRandomOffset,
+ boolean executeOnStartup,
+ boolean executeOnReload
) throws ParseException {
this.vertx = vertx;
this.redisquesAddress = redisquesAddress;
@@ -138,7 +139,9 @@ public void stop() {
private void trigger() {
for (final HttpRequest request : requests) {
- monitoringHandler.updateEnqueue();
+ if (monitoringHandler != null) {
+ monitoringHandler.updateEnqueue();
+ }
if (log.isTraceEnabled()) {
log.trace("Triggering request " + request.toJsonObject().encodePrettily());
@@ -156,7 +159,7 @@ private void trigger() {
if (event.failed()) {
if (log.isWarnEnabled()) {
log.warn("Could not enqueue request '{}' '{}'", queueName, request.getUri(),
- exceptionFactory.newException("eventBus.request('" + redisquesAddress + "', enqueOp) failed", event.cause()));
+ exceptionFactory.newException("eventBus.request('" + redisquesAddress + "', enqueOp) failed", event.cause()));
}
return;
}
diff --git a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerFactory.java b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerFactory.java
index 1a5c34f39..7639bbf09 100755
--- a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerFactory.java
+++ b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerFactory.java
@@ -15,6 +15,7 @@
import org.swisspush.gateleen.validation.ValidationException;
import org.swisspush.gateleen.validation.Validator;
+import javax.annotation.Nullable;
import java.nio.charset.Charset;
import java.text.ParseException;
import java.util.ArrayList;
@@ -54,7 +55,7 @@ public SchedulerFactory(
Vertx vertx,
RedisProvider redisProvider,
GateleenExceptionFactory exceptionFactory,
- MonitoringHandler monitoringHandler,
+ @Nullable MonitoringHandler monitoringHandler,
String schedulersSchema,
String redisquesAddress
) {
diff --git a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerResourceManager.java b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerResourceManager.java
index da8bbbe18..a953151c2 100755
--- a/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerResourceManager.java
+++ b/gateleen-scheduler/src/main/java/org/swisspush/gateleen/scheduler/SchedulerResourceManager.java
@@ -21,6 +21,7 @@
import org.swisspush.gateleen.monitoring.MonitoringHandler;
import org.swisspush.gateleen.validation.ValidationException;
+import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -44,17 +45,17 @@ public class SchedulerResourceManager implements Refreshable, LoggableResource {
private boolean logConfigurationResourceChanges = false;
public SchedulerResourceManager(Vertx vertx, RedisProvider redisProvider, final ResourceStorage storage,
- MonitoringHandler monitoringHandler, String schedulersUri) {
+ @Nullable MonitoringHandler monitoringHandler, String schedulersUri) {
this(vertx, redisProvider, storage, monitoringHandler, schedulersUri, null);
}
public SchedulerResourceManager(Vertx vertx, RedisProvider redisProvider, final ResourceStorage storage,
- MonitoringHandler monitoringHandler, String schedulersUri, Map props) {
+ @Nullable MonitoringHandler monitoringHandler, String schedulersUri, Map props) {
this(vertx, redisProvider, storage, monitoringHandler, schedulersUri, props, Address.redisquesAddress());
}
public SchedulerResourceManager(Vertx vertx, RedisProvider redisProvider, final ResourceStorage storage,
- MonitoringHandler monitoringHandler, String schedulersUri, Map props,
+ @Nullable MonitoringHandler monitoringHandler, String schedulersUri, Map props,
String redisquesAddress) {
this(vertx, redisProvider, newGateleenThriftyExceptionFactory(), storage, monitoringHandler, schedulersUri, props, redisquesAddress, Collections.emptyMap());
}
@@ -64,7 +65,7 @@ public SchedulerResourceManager(
RedisProvider redisProvider,
GateleenExceptionFactory exceptionFactory,
ResourceStorage storage,
- MonitoringHandler monitoringHandler,
+ @Nullable MonitoringHandler monitoringHandler,
String schedulersUri,
Map props,
String redisquesAddress,
diff --git a/pom.xml b/pom.xml
index b67508a75..cd834c448 100644
--- a/pom.xml
+++ b/pom.xml
@@ -74,6 +74,7 @@
2.4.10
2.12.6
2.9.0
+ 1.12.13
5.8.0
3.0.0
0.1.15
@@ -358,6 +359,11 @@
log4j-slf4j2-impl
${log4j-slf4j2.version}
+
+ io.micrometer
+ micrometer-core
+ ${micrometer.version}
+
org.swisspush
redisques