Skip to content

Commit

Permalink
Implement custom tags provider for HTTP metrics
Browse files Browse the repository at this point in the history
- Allows to implement a CustomTagsProvider interface to provide tags out
of HttpRequest objects.
- Add tests

Fixes vert-x3#89
  • Loading branch information
jotak committed Jun 3, 2021
1 parent b22535a commit a72952c
Show file tree
Hide file tree
Showing 13 changed files with 298 additions and 17 deletions.
23 changes: 23 additions & 0 deletions src/main/java/io/vertx/micrometer/MicrometerMetricsOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import io.vertx.core.json.JsonObject;
import io.vertx.core.metrics.MetricsOptions;
import io.vertx.core.spi.VertxMetricsFactory;
import io.vertx.core.spi.observability.HttpRequest;
import io.vertx.micrometer.impl.CustomTagsProvider;

import java.util.*;

Expand Down Expand Up @@ -66,6 +68,7 @@ public class MicrometerMetricsOptions extends MetricsOptions {
private VertxJmxMetricsOptions jmxMetricsOptions;
private boolean jvmMetricsEnabled;
private MetricsNaming metricsNaming;
private CustomTagsProvider<HttpRequest> requestsTagsProvider;

/**
* Creates default options for Micrometer metrics.
Expand All @@ -77,6 +80,7 @@ public MicrometerMetricsOptions() {
labelMatches = new ArrayList<>();
jvmMetricsEnabled = DEFAULT_JVM_METRICS_ENABLED;
metricsNaming = DEFAULT_METRICS_NAMING;
requestsTagsProvider = null;
}

/**
Expand All @@ -100,6 +104,7 @@ public MicrometerMetricsOptions(MicrometerMetricsOptions other) {
}
jvmMetricsEnabled = other.jvmMetricsEnabled;
metricsNaming = other.metricsNaming;
requestsTagsProvider = other.requestsTagsProvider;
}

/**
Expand Down Expand Up @@ -415,4 +420,22 @@ public MicrometerMetricsOptions setMetricsNaming(MetricsNaming metricsNaming) {
this.metricsNaming = metricsNaming;
return this;
}

/**
* @return an optional custom tags provider for HTTP requests
*/
public CustomTagsProvider<HttpRequest> getRequestsTagsProvider() {
return requestsTagsProvider;
}

/**
* Sets a custom tags provider for HTTP requests. Allows to generate custom tags for every {@code HttpRequest} object processed through the metrics SPI.
*
* @param requestsTagsProvider an object implementing the {@code CustomTagsProvider} interface for {@code HttpRequest}.
* @return a reference to this, so that the API can be used fluently
*/
public MicrometerMetricsOptions setRequestsTagsProvider(CustomTagsProvider<HttpRequest> requestsTagsProvider) {
this.requestsTagsProvider = requestsTagsProvider;
return this;
}
}
26 changes: 26 additions & 0 deletions src/main/java/io/vertx/micrometer/impl/CustomTagsProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.vertx.micrometer.impl;

import io.micrometer.core.instrument.Tag;

/**
* @author Joel Takvorian
*/
public interface CustomTagsProvider<T> {
Iterable<Tag> extractTags(T object);
}
27 changes: 17 additions & 10 deletions src/main/java/io/vertx/micrometer/impl/VertxHttpServerMetrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.vertx.micrometer.impl;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.ServerWebSocket;
import io.vertx.core.net.SocketAddress;
Expand Down Expand Up @@ -46,9 +47,11 @@ class VertxHttpServerMetrics extends VertxNetServerMetrics {
private final Timers processingTime;
private final Summaries responseBytes;
private final Gauges<LongAdder> wsConnections;
private final CustomTagsProvider<HttpRequest> customTagsProvider;

VertxHttpServerMetrics(MeterRegistry registry, MetricsNaming names) {
VertxHttpServerMetrics(MeterRegistry registry, MetricsNaming names, CustomTagsProvider<HttpRequest> customTagsProvider) {
super(registry, MetricsDomain.HTTP_SERVER, names);
this.customTagsProvider = customTagsProvider;
requests = longGauges(names.getHttpActiveRequests(), "Number of requests being processed", Label.LOCAL, Label.REMOTE, Label.HTTP_PATH, Label.HTTP_METHOD);
requestCount = counters(names.getHttpRequestsCount(), "Number of processed requests", Label.LOCAL, Label.REMOTE, Label.HTTP_ROUTE, Label.HTTP_PATH, Label.HTTP_METHOD, Label.HTTP_CODE);
requestResetCount = counters(names.getHttpRequestResetsCount(), "Number of request resets", Label.LOCAL, Label.REMOTE, Label.HTTP_PATH, Label.HTTP_METHOD);
Expand All @@ -71,26 +74,29 @@ class Instance extends VertxNetServerMetrics.Instance implements HttpServerMetri
@Override
public Handler requestBegin(String remote, HttpRequest request) {
Handler handler = new Handler(remote, request.uri(), request.method().name());
requests.get(local, remote, handler.path, handler.method).increment();
if (customTagsProvider != null) {
handler.customTags = customTagsProvider.extractTags(request);
}
requests.get(handler.customTags, local, remote, handler.path, handler.method).increment();
handler.timer = processingTime.start();
return handler;
}

@Override
public void requestReset(Handler handler) {
requestResetCount.get(local, handler.address, handler.path, handler.method).increment();
requests.get(local, handler.address, handler.path, handler.method).decrement();
requestResetCount.get(handler.customTags, local, handler.address, handler.path, handler.method).increment();
requests.get(handler.customTags, local, handler.address, handler.path, handler.method).decrement();
}

@Override
public void requestEnd(Handler handler, HttpRequest request, long bytesRead) {
requestBytes.get(local, handler.address, handler.path, handler.method).record(bytesRead);
requestBytes.get(handler.customTags, local, handler.address, handler.path, handler.method).record(bytesRead);
}

@Override
public Handler responsePushed(String remote, HttpMethod method, String uri, HttpResponse response) {
Handler handler = new Handler(remote, uri, method.name());
requests.get(local, remote, handler.path, handler.method).increment();
requests.get(handler.customTags, local, remote, handler.path, handler.method).increment();
return handler;
}

Expand All @@ -102,10 +108,10 @@ public void responseBegin(Handler handler, HttpResponse response) {
public void responseEnd(Handler handler, HttpResponse response, long bytesWritten) {
String code = String.valueOf(response.statusCode());
String handlerRoute = handler.getRoute();
handler.timer.end(local, handler.address, handlerRoute, handler.path, handler.method, code);
requestCount.get(local, handler.address, handlerRoute, handler.path, handler.method, code).increment();
requests.get(local, handler.address, handler.path, handler.method).decrement();
responseBytes.get(local, handler.address, handlerRoute, handler.path, handler.method, code).record(bytesWritten);
handler.timer.end(handler.customTags, local, handler.address, handlerRoute, handler.path, handler.method, code);
requestCount.get(handler.customTags, local, handler.address, handlerRoute, handler.path, handler.method, code).increment();
requests.get(handler.customTags, local, handler.address, handler.path, handler.method).decrement();
responseBytes.get(handler.customTags, local, handler.address, handlerRoute, handler.path, handler.method, code).record(bytesWritten);
}

@Override
Expand Down Expand Up @@ -138,6 +144,7 @@ public static class Handler {
// tracks length of resulting routes string
private int routesLength;
private Timers.EventTiming timer;
private Iterable<Tag> customTags;

Handler(String address, String path, String method) {
this.address = address;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public VertxMetricsImpl(MicrometerMetricsOptions options, BackendRegistry backen
httpClientMetrics = options.isMetricsCategoryDisabled(HTTP_CLIENT) ? null
: new VertxHttpClientMetrics(registry, names);
httpServerMetrics = options.isMetricsCategoryDisabled(HTTP_SERVER) ? null
: new VertxHttpServerMetrics(registry, names);
: new VertxHttpServerMetrics(registry, names, options.getRequestsTagsProvider());
poolMetrics = options.isMetricsCategoryDisabled(NAMED_POOLS) ? null
: new VertxPoolMetrics(registry, names);
}
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/io/vertx/micrometer/impl/meters/Counters.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.vertx.micrometer.Label;
import io.vertx.micrometer.impl.Labels;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* @author Joel Takvorian
*/
Expand All @@ -41,10 +47,17 @@ public Counters(String name,
}

public Counter get(String... values) {
return get(null, values);
}

public Counter get(Iterable<Tag> customTags, String... values) {
List<Tag> tags = customTags != null
? Stream.concat(Labels.toTags(keys, values).stream(), StreamSupport.stream(customTags.spliterator(), false)).collect(Collectors.toList())
: Labels.toTags(keys, values);
// Get or create the Counter
return Counter.builder(name)
.description(description)
.tags(Labels.toTags(keys, values))
.tags(tags)
.register(registry);
}
}
16 changes: 14 additions & 2 deletions src/main/java/io/vertx/micrometer/impl/meters/Gauges.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.vertx.micrometer.Label;
import io.vertx.micrometer.impl.Labels;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* @author Joel Takvorian
Expand Down Expand Up @@ -53,7 +58,14 @@ public Gauges(String name,
this.keys = keys;
}

public synchronized T get(String... values) {
public T get(String... values) {
return get(null, values);
}

public synchronized T get(Iterable<Tag> customTags, String... values) {
List<Tag> tags = customTags != null
? Stream.concat(Labels.toTags(keys, values).stream(), StreamSupport.stream(customTags.spliterator(), false)).collect(Collectors.toList())
: Labels.toTags(keys, values);
// This method is synchronized to make sure the "T" built via supplier will match the one passed to Gauge
// since it is stored as WeakReference in Micrometer DefaultGauge, it must not be lost.
T t = tSupplier.get();
Expand All @@ -63,7 +75,7 @@ public synchronized T get(String... values) {
// Micrometer will not register the gauge twice if it was already created.
Gauge g = Gauge.builder(name, t, dGetter)
.description(description)
.tags(Labels.toTags(keys, values))
.tags(tags)
.register(registry);
return gauges.computeIfAbsent(g.getId(), v -> t);
}
Expand Down
15 changes: 14 additions & 1 deletion src/main/java/io/vertx/micrometer/impl/meters/Summaries.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@

import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.vertx.micrometer.Label;
import io.vertx.micrometer.impl.Labels;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* @author Joel Takvorian
*/
Expand All @@ -41,10 +47,17 @@ public Summaries(String name,
}

public DistributionSummary get(String... values) {
return get(null, values);
}

public DistributionSummary get(Iterable<Tag> customTags, String... values) {
List<Tag> tags = customTags != null
? Stream.concat(Labels.toTags(keys, values).stream(), StreamSupport.stream(customTags.spliterator(), false)).collect(Collectors.toList())
: Labels.toTags(keys, values);
// Get or create the Summary
return DistributionSummary.builder(name)
.description(description)
.tags(Labels.toTags(keys, values))
.tags(tags)
.register(registry);
}
}
20 changes: 18 additions & 2 deletions src/main/java/io/vertx/micrometer/impl/meters/Timers.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
package io.vertx.micrometer.impl.meters;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.vertx.micrometer.Label;
import io.vertx.micrometer.impl.Labels;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* @author Joel Takvorian
Expand All @@ -43,10 +48,17 @@ public Timers(String name,
}

public Timer get(String... values) {
return get(null, values);
}

public Timer get(Iterable<Tag> customTags, String... values) {
List<Tag> tags = customTags != null
? Stream.concat(Labels.toTags(keys, values).stream(), StreamSupport.stream(customTags.spliterator(), false)).collect(Collectors.toList())
: Labels.toTags(keys, values);
// Get or create the Timer
return Timer.builder(name)
.description(description)
.tags(Labels.toTags(keys, values))
.tags(tags)
.register(registry);
}

Expand All @@ -64,7 +76,11 @@ private EventTiming(Timers ref) {
}

public void end(String... values) {
Timer t = ref.get(values);
end(null, values);
}

public void end(Iterable<Tag> customTags, String... values) {
Timer t = ref.get(customTags, values);
t.record(System.nanoTime() - nanoStart, TimeUnit.NANOSECONDS);
}
}
Expand Down
Loading

0 comments on commit a72952c

Please sign in to comment.