Skip to content

Commit

Permalink
Issue ReactiveX#531: Time limiter micrometer (ReactiveX#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
hexmind authored and RobWin committed Sep 2, 2019
1 parent b29acf8 commit 1319d04
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 3 deletions.
2 changes: 2 additions & 0 deletions resilience4j-micrometer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ dependencies {
compileOnly project(':resilience4j-circuitbreaker')
compileOnly project(':resilience4j-retry')
compileOnly project(':resilience4j-ratelimiter')
compileOnly project(':resilience4j-timelimiter')
testCompile project(':resilience4j-test')
testCompile project(':resilience4j-bulkhead')
testCompile project(':resilience4j-circuitbreaker')
testCompile project(':resilience4j-ratelimiter')
testCompile project(':resilience4j-timelimiter')
testCompile project(':resilience4j-retry')
testCompile project(':resilience4j-test')
testCompile project(':resilience4j-circuitbreaker')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@
public class TaggedRateLimiterMetrics extends AbstractMetrics implements MeterBinder {

/**
* Creates a new binder that uses given {@code registry} as source of retries.
* Creates a new binder that uses given {@code registry} as source of rate limiters.
*
* @param rateLimiterRegistry the source of retries
* @param rateLimiterRegistry the source of rate limiters
* @return The {@link TaggedRateLimiterMetrics} instance.
*/
public static TaggedRateLimiterMetrics ofRateLimiterRegistry(RateLimiterRegistry rateLimiterRegistry) {
return new TaggedRateLimiterMetrics(MetricNames.ofDefaults(), rateLimiterRegistry);
}

/**
* Creates a new binder that uses given {@code registry} as source of retries.
* Creates a new binder that uses given {@code registry} as source of rate limiters.
*
* @param names custom metric names
* @param rateLimiterRegistry the source of rate limiters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
/*
* Copyright 2019 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
*
* 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.github.resilience4j.micrometer.tagged;

import io.github.resilience4j.timelimiter.TimeLimiter;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

import static java.util.Objects.requireNonNull;

/**
* A micrometer binder that is used to register TimeLimiter exposed events.
*/
public class TaggedTimeLimiterMetrics extends AbstractMetrics implements MeterBinder {

/**
* Creates a new binder that uses given {@code registry} as source of time limiters.
*
* @param timeLimiterRegistry the source of time limiters
* @return The {@link TaggedTimeLimiterMetrics} instance.
*/
public static TaggedTimeLimiterMetrics ofTimeLimiterRegistry(TimeLimiterRegistry timeLimiterRegistry) {
return new TaggedTimeLimiterMetrics(MetricNames.ofDefaults(), timeLimiterRegistry);
}

/**
* Creates a new binder that uses given {@code registry} as source of time limiters.
*
* @param names custom metric names
* @param timeLimiterRegistry the source of time limiters
* @return The {@link TaggedTimeLimiterMetrics} instance.
*/
public static TaggedTimeLimiterMetrics ofTimeLimiterRegistry(MetricNames names, TimeLimiterRegistry timeLimiterRegistry) {
return new TaggedTimeLimiterMetrics(names, timeLimiterRegistry);
}

private final MetricNames names;
private final TimeLimiterRegistry timeLimiterRegistry;

private TaggedTimeLimiterMetrics(MetricNames names, TimeLimiterRegistry timeLimiterRegistry) {
super();
this.names = Objects.requireNonNull(names);
this.timeLimiterRegistry = Objects.requireNonNull(timeLimiterRegistry);
}

@Override
public void bindTo(MeterRegistry registry) {
for (TimeLimiter timeLimiter : timeLimiterRegistry.getAllTimeLimiters()) {
addMetrics(registry, timeLimiter);
}
timeLimiterRegistry.getEventPublisher().onEntryAdded(event -> addMetrics(registry, event.getAddedEntry()));
timeLimiterRegistry.getEventPublisher().onEntryRemoved(event -> removeMetrics(registry, event.getRemovedEntry().getName()));
timeLimiterRegistry.getEventPublisher().onEntryReplaced(event -> {
removeMetrics(registry, event.getOldEntry().getName());
addMetrics(registry, event.getNewEntry());
});
}

private void addMetrics(MeterRegistry registry, TimeLimiter timeLimiter) {
Counter successes = Counter.builder(names.getSuccessfulMetricName())
.description("The number of successful calls")
.tag(TagNames.NAME, timeLimiter.getName())
.register(registry);
Counter failures = Counter.builder(names.getFailedMetricName())
.description("The number of failed calls")
.tag(TagNames.NAME, timeLimiter.getName())
.register(registry);
Counter timeouts = Counter.builder(names.getTimeoutMetricName())
.description("The number of timed out calls")
.tag(TagNames.NAME, timeLimiter.getName())
.register(registry);

timeLimiter.getEventPublisher()
.onSuccess(event -> successes.increment())
.onError(event -> failures.increment())
.onTimeout(event -> timeouts.increment());

List<Meter.Id> ids = Arrays.asList(successes.getId(), failures.getId(), timeouts.getId());
meterIdMap.put(timeLimiter.getName(), new HashSet<>(ids));
}

/**
* Defines possible configuration for metric names.
*/
public static class MetricNames {

private static final String DEFAULT_PREFIX = "resilience4j.timelimiter";
public static final String SUCCESSFUL_METRIC_NAME = DEFAULT_PREFIX + ".successful";
public static final String FAILED_METRIC_NAME = DEFAULT_PREFIX + ".failed";
public static final String TIMEOUT_METRIC_NAME = DEFAULT_PREFIX + ".timeout";

/**
* Returns a builder for creating custom metric names.
* Note that names have default values, so only desired metrics can be renamed.
*
* @return The builder.
*/
public static Builder custom() {
return new Builder();
}

/**
* Returns default metric names.
*
* @return The default {@link MetricNames} instance.
*/
public static MetricNames ofDefaults() {
return new MetricNames();
}

private String successfulMetricName = SUCCESSFUL_METRIC_NAME;
private String failedMetricName = FAILED_METRIC_NAME;
private String timeoutMetricName = TIMEOUT_METRIC_NAME;

/**
* Returns the metric name for successful calls, defaults to {@value SUCCESSFUL_METRIC_NAME}.
*
* @return The successful calls metric name.
*/
public String getSuccessfulMetricName() {
return successfulMetricName;
}

/**
* Returns the metric name for failed calls, defaults to {@value FAILED_METRIC_NAME}.
*
* @return The failed calls metric name.
*/
public String getFailedMetricName() {
return failedMetricName;
}

/**
* Returns the metric name for timed out calls, defaults to {@value TIMEOUT_METRIC_NAME}.
*
* @return The timed out calls metric name.
*/
public String getTimeoutMetricName() {
return timeoutMetricName;
}

/**
* Helps building custom instance of {@link MetricNames}.
*/
public static class Builder {

private final MetricNames metricNames = new MetricNames();

/**
* Overrides the default metric name {@value MetricNames#SUCCESSFUL_METRIC_NAME} with a given one.
*
* @param successfulMetricName The successful calls metric name.
* @return The builder.
*/
public Builder successfulMetricName(String successfulMetricName) {
metricNames.successfulMetricName = requireNonNull(successfulMetricName);
return this;
}

/**
* Overrides the default metric name {@value MetricNames#FAILED_METRIC_NAME} with a given one.
*
* @param failedMetricName The failed calls metric name.
* @return The builder.
*/
public Builder failedMetricName(String failedMetricName) {
metricNames.failedMetricName = requireNonNull(failedMetricName);
return this;
}

/**
* Overrides the default metric name {@value MetricNames#TIMEOUT_METRIC_NAME} with a given one.
*
* @param timeoutMetricName The timed out calls metric name.
* @return The builder.
*/
public Builder timeoutMetricName(String timeoutMetricName) {
metricNames.timeoutMetricName = requireNonNull(timeoutMetricName);
return this;
}

/**
* Builds {@link MetricNames} instance.
*
* @return The built {@link MetricNames} instance.
*/
public MetricNames build() {
return metricNames;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ static Optional<Counter> findCounterByKindAndNameTags(Collection<Counter> counte
.findAny();
}

static Optional<Counter> findCounterByNamesTag(Collection<Counter> gauges, String name) {
return gauges.stream()
.filter(g -> name.equals(g.getId().getTag(TagNames.NAME)))
.findAny();
}

static Optional<Gauge> findGaugeByNamesTag(Collection<Gauge> gauges, String name) {
return gauges.stream()
.filter(g -> name.equals(g.getId().getTag(TagNames.NAME)))
Expand Down
Loading

0 comments on commit 1319d04

Please sign in to comment.