Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable metrics registration #1

Merged
merged 1 commit into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>5.9.0</junit.version>
</properties>

<build>
Expand Down Expand Up @@ -199,18 +200,30 @@
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.3</version>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.33.2</version>
<version>2.34.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.0</version>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.23.1</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.rasklaad.wiremock.metrics;

public class MetricsConfiguration {

private boolean useMappingUrlPattern;
private boolean useRequestUrl;
private boolean registerNotMatchedRequests;

private boolean ignoreQueryParams;

private boolean registerAnyUrlMappingAsRequestUrl;

MetricsConfiguration() {

}
public MetricsConfiguration useRequestUrl() {
useRequestUrl = true;
return this;
}

public MetricsConfiguration useMappingUrlPattern() {
useMappingUrlPattern = true;
return this;
}

public MetricsConfiguration registerNotMatchedRequests() {
registerNotMatchedRequests = true;
return this;
}

public MetricsConfiguration registerAnyUrlMappingAsRequestUrl() {
registerAnyUrlMappingAsRequestUrl = true;
return this;
}

public MetricsConfiguration ignoreQueryParams() {
ignoreQueryParams = true;
return this;
}

static MetricsConfiguration defaultConfiguration() {
return new MetricsConfiguration().useRequestUrl();
}

void validate() {
if (useMappingUrlPattern && useRequestUrl) {
throw new IllegalStateException("You can't use both url path and url pattern");
}
if (!useMappingUrlPattern && !useRequestUrl) {
throw new IllegalStateException("You must use either url path or url pattern");
}
}
boolean shouldUseMappingUrlPattern() {
return useMappingUrlPattern;
}

boolean shouldUseRequestUrl() {
return useRequestUrl;
}

boolean shouldRegisterNotMatchedRequests() {
return registerNotMatchedRequests;
}

boolean shouldRegisterAnyUrlMappingAsRequestUrl() {
return registerAnyUrlMappingAsRequestUrl;
}

boolean shouldIgnoreQueryParams() {
return ignoreQueryParams;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.RequestMethod;
import com.github.tomakehurst.wiremock.http.ResponseDefinition;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.prometheus.PrometheusMeterRegistry;

import java.net.HttpURLConnection;
import java.util.List;
import java.util.stream.Collectors;

public class MetricsEndpointExtension implements AdminApiExtension {
public static final String EXTENSION_NAME = "metrics-endpoint-extension";

private final AdminTask adminTask;

public MetricsEndpointExtension() {
adminTask = new PrometheusEndpointAdminTask();
}
@Override
public void contributeAdminApiRoutes(Router router) {
router.add(RequestMethod.GET, "/prometheus-metrics", new PrometheusEndpointAdminTask());
router.add(RequestMethod.GET, "/prometheus-metrics", adminTask);
}

@Override
Expand All @@ -30,13 +39,14 @@ private final static class PrometheusEndpointAdminTask implements AdminTask {
private final PrometheusMeterRegistry prometheusMeterRegistry;

private PrometheusEndpointAdminTask() {
prometheusMeterRegistry = (PrometheusMeterRegistry) Metrics.globalRegistry.getRegistries()
List<MeterRegistry> registries = Metrics.globalRegistry.getRegistries()
.stream()
.filter(registry -> registry instanceof PrometheusMeterRegistry)
.findFirst()
.orElseThrow(() -> new IllegalStateException("PrometheusMeterRegistry not found," +
" you should define com.rasklaad.wiremock.metrics.PrometheusMetricsExtension" +
" in your wiremock extensions"));
.collect(Collectors.toList());
if (registries.size() != 1) {
throw new IllegalStateException("Expected exactly one PrometheusMeterRegistry, found " + registries.size());
}
prometheusMeterRegistry = (PrometheusMeterRegistry) registries.get(0);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.github.tomakehurst.wiremock.core.Admin;
import com.github.tomakehurst.wiremock.extension.PostServeAction;
import com.github.tomakehurst.wiremock.http.LoggedResponse;
import com.github.tomakehurst.wiremock.matching.UrlPattern;
import com.github.tomakehurst.wiremock.stubbing.ServeEvent;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import com.github.tomakehurst.wiremock.verification.LoggedRequest;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.Metrics;
Expand All @@ -16,9 +16,12 @@
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;

import java.net.URI;

public class PrometheusMetricsExtension extends PostServeAction {
public static final String EXTENSION_NAME = "prometheus-metrics-extension";
private final PrometheusMeterRegistry registry;
private MetricsConfiguration configuration;

public PrometheusMetricsExtension() {
registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
Expand All @@ -27,58 +30,108 @@ public PrometheusMetricsExtension() {
new ProcessorMetrics().bindTo(registry);
new JvmGcMetrics().bindTo(registry);
new JvmMemoryMetrics().bindTo(registry);
configuration = MetricsConfiguration.defaultConfiguration();
}

public PrometheusMetricsExtension(MetricsConfiguration configuration) {
this();
configuration.validate();
this.configuration = configuration;
}

@Override
public void doGlobalAction(ServeEvent serveEvent, Admin admin) {
super.doGlobalAction(serveEvent, admin);
StubMapping mapping = serveEvent.getStubMapping();
if (mapping != null) {
Timing timing = serveEvent.getTiming();
LoggedRequest request = serveEvent.getRequest();
LoggedResponse response = serveEvent.getResponse();
String urlPath = request.getUrl();
String method = request.getMethod().getName();
String status = String.valueOf(response.getStatus());

DistributionSummary totalTimeSummary = DistributionSummary.builder("wiremock.request.totalTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Request time latency")
.tags("path", urlPath,"method", method, "status", status)
.register(Metrics.globalRegistry);

DistributionSummary processingTimeSummary = DistributionSummary.builder("wiremock.request.processingTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Processing time latency")
.tags("path", urlPath,"method", method, "status", status)
.register(registry);


DistributionSummary serveTimeSummary = DistributionSummary.builder("wiremock.request.serveTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Serve time latency")
.tags("path", urlPath,"method", method, "status", status)
.register(registry);

DistributionSummary responseSendTime = DistributionSummary.builder("wiremock.request.responseSendTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Response send time latency")
.tags("path", urlPath,"method", method, "status", status)
.register(registry);

totalTimeSummary.record(timing.getTotalTime());
processingTimeSummary.record(timing.getProcessTime());
serveTimeSummary.record(timing.getServeTime());
responseSendTime.record(timing.getResponseSendTime());
Timing timing = serveEvent.getTiming();
LoggedRequest request = serveEvent.getRequest();
LoggedResponse response = serveEvent.getResponse();

String requestUrlPath = request.getUrl();
String method = request.getMethod().getName();
String status = String.valueOf(response.getStatus());

if (!serveEvent.getWasMatched()) {
if (configuration.shouldRegisterNotMatchedRequests()) {
registerByUrlPath(timing, requestUrlPath, method, status);
}
return;
}

if (configuration.shouldUseRequestUrl()) {
registerByUrlPath(timing, requestUrlPath, method, status);
return;
}

UrlPattern urlPattern = serveEvent.getStubMapping().getRequest().getUrlMatcher();
if (configuration.shouldUseMappingUrlPattern()) {
if (urlPattern == UrlPattern.ANY && configuration.shouldRegisterAnyUrlMappingAsRequestUrl()) {
registerByUrlPath(timing, requestUrlPath, method, status);
} else {
registerByUrlMapping(timing, urlPattern, method, status);
}
}

}

private void registerByUrlMapping(Timing timing, UrlPattern urlPattern, String method, String status) {
String mappingUrlPath = urlPattern.getExpected();
if (configuration.shouldIgnoreQueryParams()) {
mappingUrlPath = URI.create(mappingUrlPath).getPath();
}
register(timing, mappingUrlPath, method, status);

}

private void registerByUrlPath(Timing timing, String urlPath, String method, String statusCode) {
String path = urlPath;
if (configuration.shouldIgnoreQueryParams()) {
path = URI.create(urlPath).getPath();
}
register(timing, path, method, statusCode);
}

private void register(Timing timing, String path, String method, String statusCode) {
DistributionSummary totalTimeSummary = DistributionSummary.builder("wiremock.request.totalTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Request time latency")
.tags("path", path, "method", method, "status", statusCode)
.register(Metrics.globalRegistry);

DistributionSummary processingTimeSummary = DistributionSummary.builder("wiremock.request.processingTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Processing time latency")
.tags("path", path, "method", method, "status", statusCode)
.register(registry);


DistributionSummary serveTimeSummary = DistributionSummary.builder("wiremock.request.serveTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Serve time latency")
.tags("path", path, "method", method, "status", statusCode)
.register(registry);

DistributionSummary responseSendTime = DistributionSummary.builder("wiremock.request.responseSendTime")
.baseUnit("ms")
.publishPercentiles(0.5, 0.9, 0.95, 0.99)
.description("Response send time latency")
.tags("path", path, "method", method, "status", statusCode)
.register(registry);

totalTimeSummary.record(timing.getTotalTime());
processingTimeSummary.record(timing.getProcessTime());
serveTimeSummary.record(timing.getServeTime());
responseSendTime.record(timing.getResponseSendTime());
}

@Override
public String getName() {
return EXTENSION_NAME;
}

public static MetricsConfiguration options() {
return new MetricsConfiguration();
}
}
Loading