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

Improvements to the HttpMeterFilterFactory #766

Merged
Show file tree
Hide file tree
Changes from 4 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

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@
import io.micrometer.core.instrument.config.MeterFilter;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.configuration.metrics.binder.web.config.HttpClientMeterConfig;
import io.micronaut.configuration.metrics.binder.web.config.HttpMeterConfig;
import io.micronaut.configuration.metrics.binder.web.config.HttpServerMeterConfig;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import io.micronaut.context.annotation.Value;
import io.micronaut.core.util.ArrayUtils;
import jakarta.inject.Singleton;

import static io.micronaut.configuration.metrics.binder.web.WebMetricsPublisher.METRIC_HTTP_CLIENT_REQUESTS;
import static io.micronaut.configuration.metrics.binder.web.WebMetricsPublisher.METRIC_HTTP_SERVER_REQUESTS;
import java.util.Arrays;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;
import static io.micronaut.core.util.StringUtils.FALSE;

Expand All @@ -41,41 +42,54 @@
@Requires(property = WebMetricsPublisher.ENABLED, notEquals = FALSE)
public class HttpMeterFilterFactory {

public static final double SECONDS_TO_NANOS = 1_000_000_000d;

/**
* Configure new MeterFilter for http.server.requests metrics.
*
* @param percentiles The percentiles
* @param serverMeterConfig The HttpMeter configuration
* @return A MeterFilter
*/
@Bean
@Singleton
@Requires(property = MICRONAUT_METRICS_BINDERS + ".web.server.percentiles")
MeterFilter addServerPercentileMeterFilter(@Value("${" + MICRONAUT_METRICS_BINDERS + ".web.server.percentiles}") Double[] percentiles) {
return getMeterFilter(percentiles, METRIC_HTTP_SERVER_REQUESTS);
@Requires(property = MICRONAUT_METRICS_BINDERS + ".web.server")
MeterFilter addServerPercentileMeterFilter(HttpServerMeterConfig serverMeterConfig) {
return getMeterFilter(serverMeterConfig, WebMetricsPublisher.METRIC_HTTP_SERVER_REQUESTS);
}

/**
* Configure new MeterFilter for http.client.requests metrics.
*
* @param percentiles The percentiles
* @param clientMeterConfig The HttpMeter configuration
* @return A MeterFilter
*/
@Bean
@Singleton
@Requires(property = MICRONAUT_METRICS_BINDERS + ".web.client.percentiles")
MeterFilter addClientPercentileMeterFilter(@Value("${" + MICRONAUT_METRICS_BINDERS + ".web.client.percentiles}") Double[] percentiles) {
return getMeterFilter(percentiles, METRIC_HTTP_CLIENT_REQUESTS);
@Requires(property = MICRONAUT_METRICS_BINDERS + ".web.client")
MeterFilter addClientPercentileMeterFilter(HttpClientMeterConfig clientMeterConfig) {
return getMeterFilter(clientMeterConfig, WebMetricsPublisher.METRIC_HTTP_CLIENT_REQUESTS);
}

private MeterFilter getMeterFilter(Double[] percentiles, String metricNamePrefix) {
private MeterFilter getMeterFilter(HttpMeterConfig meterConfig, String metricNamePrefix) {
return new MeterFilter() {
@Override
public DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {
if (id.getName().startsWith(metricNamePrefix)) {
return DistributionStatisticConfig.builder()
.percentiles((double[]) ArrayUtils.toPrimitiveArray(percentiles))
.build()
.merge(config);
var builder = DistributionStatisticConfig.builder()
.percentiles()
.percentiles(Arrays.stream(meterConfig.getPercentiles()).mapToDouble(Double::doubleValue).toArray())
.serviceLevelObjectives(Arrays.stream(meterConfig.getSlos()).mapToDouble(d -> d * SECONDS_TO_NANOS).toArray())
.percentilesHistogram(meterConfig.getHistogram());

if (meterConfig.getMin() != null) {
builder.minimumExpectedValue(meterConfig.getMin() * SECONDS_TO_NANOS);
}

if (meterConfig.getMax() != null) {
builder.maximumExpectedValue(meterConfig.getMax() * SECONDS_TO_NANOS);
}

return builder.build().merge(config);
}
return config;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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.micronaut.configuration.metrics.binder.web.config;

import io.micronaut.context.annotation.ConfigurationProperties;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;

/**
* Http client meter configuration.
*/
@ConfigurationProperties(MICRONAUT_METRICS_BINDERS + ".web.client")
public class HttpClientMeterConfig extends HttpMeterConfig { }
lcavadas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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.micronaut.configuration.metrics.binder.web.config;

/**
* Http meter configuration.
*/
public abstract class HttpMeterConfig {
lcavadas marked this conversation as resolved.
Show resolved Hide resolved

private Double[] percentiles = new Double[]{};
private Boolean histogram = false;
private Double min = null;
private Double max = null;
private Double[] slos = new Double[]{};

/**
* Default is empty.
* @return The percentiles. Specify in CSV format, ex: "0.95,0.99".
*/
public Double[] getPercentiles() {
return percentiles;
}

/**
* Default is empty.
* @param percentiles The percentiles. Specify in CSV format, ex: "0.95,0.99".
*/
public void setPercentiles(Double[] percentiles) {
this.percentiles = percentiles;
}

/**
* Default: false.
* @return If a histogram should be published.
*/
public Boolean getHistogram() {
return histogram;
}

/**
* Default: false.
* @param histogram If a histogram should be published.
*/
public void setHistogram(Boolean histogram) {
this.histogram = histogram;
}

/**
* Default: Micrometer default value (0.001).
* @return The minimum time (in s) value expected.
*/
public Double getMin() {
return min;
}

/**
* Default: Micrometer default value (0.001).
* @param min The minimum time (in s) value expected.
*/
public void setMin(Double min) {
this.min = min;
}

/**
* Default: Micrometer default value (30).
* @return The maximum time (in s) value expected.
*/
public Double getMax() {
return max;
}

/**
* Default: Micrometer default value (30).
* @param max The maximum time (in s) value expected.
*/
public void setMax(Double max) {
this.max = max;
}

/**
* Default is empty.
* @return The user-defined service levels objectives (in s) to create. Specify in CSV format, ex: "0.1,0.4".
*/
public Double[] getSlos() {
return slos;
}

/**
* Default is empty.
* @param slos The user-defined service levels objectives (in s) to create. Specify in CSV format, ex: "0.1,0.4".
*/
public void setSlos(Double[] slos) {
this.slos = slos;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2017-2024 original authors
*
* 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
*
* https://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.micronaut.configuration.metrics.binder.web.config;

import io.micronaut.context.annotation.ConfigurationProperties;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;

/**
* Http server meter configuration.
*/
@ConfigurationProperties(MICRONAUT_METRICS_BINDERS + ".web.server")
public class HttpServerMeterConfig extends HttpMeterConfig { }
lcavadas marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import groovy.transform.InheritConstructors
import io.micrometer.common.lang.NonNull
import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.Timer
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig
import io.micrometer.core.instrument.distribution.HistogramSnapshot
import io.micrometer.core.instrument.search.MeterNotFoundException
import io.micronaut.context.ApplicationContext
Expand Down Expand Up @@ -49,14 +50,27 @@ class HttpMetricsSpec extends Specification {
Timer clientTimer = registry.get(WebMetricsPublisher.METRIC_HTTP_CLIENT_REQUESTS).tags('uri', '/test-http-metrics').timer()
HistogramSnapshot serverSnapshot = serverTimer.takeSnapshot()
HistogramSnapshot clientSnapshot = clientTimer.takeSnapshot()
DistributionStatisticConfig serverDistributionConfig = serverTimer.getMetaPropertyValues().find { it.name.equals('distributionStatisticConfig') }.value as DistributionStatisticConfig
DistributionStatisticConfig clientDistributionConfig = clientTimer.getMetaPropertyValues().find { it.name.equals('distributionStatisticConfig') }.value as DistributionStatisticConfig

then:
serverTimer.count() == 1
clientTimer.count() == 1

serverSnapshot.percentileValues().length == serverPercentilesCount
clientSnapshot.percentileValues().length == clientPercentilesCount
serverTimer.getMetaPropertyValues().find { it.name.equals('distributionStatisticConfig') }.value.percentileHistogram == serverHistogram
clientTimer.getMetaPropertyValues().find { it.name.equals('distributionStatisticConfig') }.value.percentileHistogram == clientHistogram

serverDistributionConfig.percentileHistogram == serverHistogram
clientDistributionConfig.percentileHistogram == clientHistogram

serverDistributionConfig.getServiceLevelObjectiveBoundaries()?.length == serverSlosCount
clientDistributionConfig.getServiceLevelObjectiveBoundaries()?.length == clientSlosCount

serverDistributionConfig.minimumExpectedValueAsDouble == serverMin
serverDistributionConfig.maximumExpectedValueAsDouble == serverMax

clientDistributionConfig.minimumExpectedValueAsDouble == clientMin
clientDistributionConfig.maximumExpectedValueAsDouble == clientMax

when: "A request is sent to the root route"

Expand Down Expand Up @@ -137,13 +151,21 @@ class HttpMetricsSpec extends Specification {
embeddedServer.close()

where:
cfg | setting | serverPercentilesCount | clientPercentilesCount| serverHistogram | clientHistogram
MICRONAUT_METRICS_BINDERS + ".web.client.percentiles" | "0.95,0.99" | 0 | 2 | null | null
MICRONAUT_METRICS_BINDERS + ".web.server.percentiles" | "0.95,0.99" | 2 | 0 | null | null
MICRONAUT_METRICS_BINDERS + ".web.server.histogram" | "true" | 0 | 0 | true | null
MICRONAUT_METRICS_BINDERS + ".web.server.histogram" | "false" | 0 | 0 | false | null
MICRONAUT_METRICS_BINDERS + ".web.client.histogram" | "true" | 0 | 0 | null | true
MICRONAUT_METRICS_BINDERS + ".web.client.histogram" | "false" | 0 | 0 | null | false
cfg | setting | serverPercentilesCount | clientPercentilesCount | serverSlosCount | clientSlosCount | serverHistogram | clientHistogram | serverMin | serverMax | clientMin | clientMax
// Server
MICRONAUT_METRICS_BINDERS + ".web.server.percentiles" | "0.95,0.99" | 2 | 0 | 0 | null | false | null | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.server.histogram" | "true" | 0 | 0 | 0 | null | true | null | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.server.histogram" | "false" | 0 | 0 | 0 | null | false | null | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.server.min" | 0.1 | 0 | 0 | 0 | null | false | null | 1.0E8 | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.server.max" | 60 | 0 | 0 | 0 | null | false | null | 1000000d | 6.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.server.slos" | "0.1,0.2,0.5" | 0 | 0 | 3 | null | false | null | 1000000d | 3.0E10 | 1000000d | 3.0E10
// Client
MICRONAUT_METRICS_BINDERS + ".web.client.percentiles" | "0.95,0.99" | 0 | 2 | null | 0 | null | false | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.client.histogram" | "true" | 0 | 0 | null | 0 | null | true | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.client.histogram" | "false" | 0 | 0 | null | 0 | null | false | 1000000d | 3.0E10 | 1000000d | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.client.min" | 0.1 | 0 | 0 | null | 0 | null | false | 1000000d | 3.0E10 | 1.0E8 | 3.0E10
MICRONAUT_METRICS_BINDERS + ".web.client.max" | 60 | 0 | 0 | null | 0 | null | false | 1000000d | 3.0E10 | 1000000d | 6.0E10
MICRONAUT_METRICS_BINDERS + ".web.client.slos" | "0.1,0.2,0.5" | 0 | 0 | null | 3 | null | false | 1000000d | 3.0E10 | 1000000d | 3.0E10
}

void "test client / server metrics ignored uris for client errors"() {
Expand Down
Loading
Loading