forked from ReactiveX/RxJava
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue ReactiveX#531: Added Dropwizard metrics for TimeLimiter (Reacti…
- Loading branch information
Showing
15 changed files
with
849 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
resilience4j-metrics/src/main/java/io/github/resilience4j/metrics/TimeLimiterMetrics.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/* | ||
* | ||
* 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.metrics; | ||
|
||
import io.github.resilience4j.timelimiter.TimeLimiter; | ||
import io.github.resilience4j.timelimiter.TimeLimiterRegistry; | ||
import io.vavr.collection.Array; | ||
|
||
import java.util.Map; | ||
|
||
import com.codahale.metrics.Counter; | ||
import com.codahale.metrics.Metric; | ||
import com.codahale.metrics.MetricRegistry; | ||
import com.codahale.metrics.MetricSet; | ||
|
||
import static com.codahale.metrics.MetricRegistry.name; | ||
import static io.github.resilience4j.timelimiter.utils.MetricNames.DEFAULT_PREFIX; | ||
import static io.github.resilience4j.timelimiter.utils.MetricNames.FAILED; | ||
import static io.github.resilience4j.timelimiter.utils.MetricNames.SUCCESSFUL; | ||
import static io.github.resilience4j.timelimiter.utils.MetricNames.TIMEOUT; | ||
import static java.util.Objects.requireNonNull; | ||
|
||
/** | ||
* An adapter which exports TimeLimiter's events as Dropwizard Metrics. | ||
*/ | ||
public class TimeLimiterMetrics implements MetricSet { | ||
|
||
private static final String PREFIX_NULL = "Prefix must not be null"; | ||
private static final String ITERABLE_NULL = "TimeLimiters iterable must not be null"; | ||
|
||
private final MetricRegistry metricRegistry; | ||
|
||
private TimeLimiterMetrics(Iterable<TimeLimiter> timeLimiters) { | ||
this(DEFAULT_PREFIX, timeLimiters, new MetricRegistry()); | ||
} | ||
|
||
private TimeLimiterMetrics(String prefix, Iterable<TimeLimiter> timeLimiters, MetricRegistry metricRegistry) { | ||
requireNonNull(prefix, PREFIX_NULL); | ||
requireNonNull(timeLimiters, ITERABLE_NULL); | ||
requireNonNull(metricRegistry); | ||
this.metricRegistry = metricRegistry; | ||
timeLimiters.forEach(timeLimiter -> { | ||
String name = timeLimiter.getName(); | ||
Counter successes = metricRegistry.counter(name(prefix, name, SUCCESSFUL)); | ||
Counter failures = metricRegistry.counter(name(prefix, name, FAILED)); | ||
Counter timeouts = metricRegistry.counter(name(prefix, name, TIMEOUT)); | ||
timeLimiter.getEventPublisher().onSuccess(event -> successes.inc()); | ||
timeLimiter.getEventPublisher().onError(event -> failures.inc()); | ||
timeLimiter.getEventPublisher().onTimeout(event -> timeouts.inc()); | ||
} | ||
); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with specified metrics names prefix and | ||
* a {@link TimeLimiterRegistry} as a source. | ||
* | ||
* @param prefix the prefix of metrics names | ||
* @param timeLimiterRegistry the registry of time limiters | ||
* @param metricRegistry the metric registry | ||
*/ | ||
public static TimeLimiterMetrics ofTimeLimiterRegistry(String prefix, TimeLimiterRegistry timeLimiterRegistry, MetricRegistry metricRegistry) { | ||
return new TimeLimiterMetrics(prefix, timeLimiterRegistry.getAllTimeLimiters(), metricRegistry); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with specified metrics names prefix and | ||
* a {@link TimeLimiterRegistry} as a source. | ||
* | ||
* @param prefix the prefix of metrics names | ||
* @param timeLimiterRegistry the registry of time limiters | ||
*/ | ||
public static TimeLimiterMetrics ofTimeLimiterRegistry(String prefix, TimeLimiterRegistry timeLimiterRegistry) { | ||
return new TimeLimiterMetrics(prefix, timeLimiterRegistry.getAllTimeLimiters(), new MetricRegistry()); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with | ||
* a {@link TimeLimiterRegistry} as a source. | ||
* | ||
* @param timeLimiterRegistry the registry of time limiters | ||
* @param metricRegistry the metric registry | ||
*/ | ||
public static TimeLimiterMetrics ofTimeLimiterRegistry(TimeLimiterRegistry timeLimiterRegistry, MetricRegistry metricRegistry) { | ||
return new TimeLimiterMetrics(DEFAULT_PREFIX, timeLimiterRegistry.getAllTimeLimiters(), metricRegistry); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with | ||
* a {@link TimeLimiterRegistry} as a source. | ||
* | ||
* @param timeLimiterRegistry the registry of time limiters | ||
*/ | ||
public static TimeLimiterMetrics ofTimeLimiterRegistry(TimeLimiterRegistry timeLimiterRegistry) { | ||
return new TimeLimiterMetrics(timeLimiterRegistry.getAllTimeLimiters()); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with | ||
* an {@link Iterable} of time limiters as a source. | ||
* | ||
* @param timeLimiters the time limiters | ||
*/ | ||
public static TimeLimiterMetrics ofIterable(Iterable<TimeLimiter> timeLimiters) { | ||
return new TimeLimiterMetrics(timeLimiters); | ||
} | ||
|
||
/** | ||
* Creates a new instance {@link TimeLimiterMetrics} with | ||
* an {@link Iterable} of time limiters as a source. | ||
* | ||
* @param timeLimiters the time limiters | ||
*/ | ||
public static TimeLimiterMetrics ofIterable(String prefix, Iterable<TimeLimiter> timeLimiters) { | ||
return new TimeLimiterMetrics(prefix, timeLimiters, new MetricRegistry()); | ||
} | ||
|
||
|
||
/** | ||
* Creates a new instance of {@link TimeLimiterMetrics} with a time limiter as a source. | ||
* | ||
* @param timeLimiter the time limiter | ||
*/ | ||
public static TimeLimiterMetrics ofTimeLimiter(TimeLimiter timeLimiter) { | ||
return new TimeLimiterMetrics(Array.of(timeLimiter)); | ||
} | ||
|
||
@Override | ||
public Map<String, Metric> getMetrics() { | ||
return metricRegistry.getMetrics(); | ||
} | ||
} |
157 changes: 157 additions & 0 deletions
157
...lience4j-metrics/src/test/java/io/github/resilience4j/metrics/TimeLimiterMetricsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
/* | ||
* | ||
* 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.metrics; | ||
|
||
import io.github.resilience4j.timelimiter.TimeLimiter; | ||
import io.github.resilience4j.timelimiter.TimeLimiterConfig; | ||
import io.github.resilience4j.timelimiter.TimeLimiterRegistry; | ||
import io.vavr.control.Try; | ||
|
||
import java.time.Duration; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.function.Supplier; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import com.codahale.metrics.MetricRegistry; | ||
|
||
import static io.github.resilience4j.metrics.assertion.MetricRegistryAssert.assertThat; | ||
import static org.assertj.core.api.BDDAssertions.then; | ||
|
||
public class TimeLimiterMetricsTest { | ||
|
||
private static final String DEFAULT_PREFIX = "resilience4j.timelimiter.UNDEFINED."; | ||
private static final String SUCCESSFUL = "successful"; | ||
private static final String FAILED = "failed"; | ||
private static final String TIMEOUT = "timeout"; | ||
|
||
private MetricRegistry metricRegistry; | ||
|
||
@Before | ||
public void setUp() { | ||
metricRegistry = new MetricRegistry(); | ||
} | ||
|
||
@Test | ||
public void shouldRegisterMetrics() throws Exception { | ||
TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.ofDefaults(); | ||
TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter("testLimit"); | ||
metricRegistry.registerAll(TimeLimiterMetrics.ofTimeLimiterRegistry(timeLimiterRegistry)); | ||
String expectedPrefix = "resilience4j.timelimiter.testLimit."; | ||
Supplier<CompletableFuture<String>> futureSupplier = () -> | ||
CompletableFuture.completedFuture("Hello world"); | ||
|
||
String result = timeLimiter.decorateFutureSupplier(futureSupplier).call(); | ||
|
||
then(result).isEqualTo("Hello world"); | ||
assertThat(metricRegistry).hasMetricsSize(3); | ||
assertThat(metricRegistry).counter(expectedPrefix + SUCCESSFUL) | ||
.hasValue(1L); | ||
assertThat(metricRegistry).counter(expectedPrefix + FAILED) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(expectedPrefix + TIMEOUT) | ||
.hasValue(0L); | ||
} | ||
|
||
@Test | ||
public void shouldUseCustomPrefix() throws Exception { | ||
TimeLimiterRegistry timeLimiterRegistry = TimeLimiterRegistry.ofDefaults(); | ||
TimeLimiter timeLimiter = timeLimiterRegistry.timeLimiter("testLimit"); | ||
metricRegistry.registerAll(TimeLimiterMetrics.ofIterable("testPre", timeLimiterRegistry.getAllTimeLimiters())); | ||
String expectedPrefix = "testPre.testLimit."; | ||
Supplier<CompletableFuture<String>> futureSupplier = () -> | ||
CompletableFuture.completedFuture("Hello world"); | ||
|
||
String result = timeLimiter.decorateFutureSupplier(futureSupplier).call(); | ||
|
||
then(result).isEqualTo("Hello world"); | ||
assertThat(metricRegistry).hasMetricsSize(3); | ||
assertThat(metricRegistry).counter(expectedPrefix + SUCCESSFUL) | ||
.hasValue(1L); | ||
assertThat(metricRegistry).counter(expectedPrefix + FAILED) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(expectedPrefix + TIMEOUT) | ||
.hasValue(0L); | ||
} | ||
|
||
@Test | ||
public void shouldRecordSuccesses() { | ||
TimeLimiter timeLimiter = TimeLimiter.of(TimeLimiterConfig.ofDefaults()); | ||
metricRegistry.registerAll(TimeLimiterMetrics.ofTimeLimiter(timeLimiter)); | ||
Supplier<CompletableFuture<String>> futureSupplier = () -> | ||
CompletableFuture.completedFuture("Hello world"); | ||
|
||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
|
||
assertThat(metricRegistry).hasMetricsSize(3); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + SUCCESSFUL) | ||
.hasValue(2L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + FAILED) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + TIMEOUT) | ||
.hasValue(0L); | ||
} | ||
|
||
@Test | ||
public void shouldRecordErrors() { | ||
TimeLimiter timeLimiter = TimeLimiter.of(TimeLimiterConfig.ofDefaults()); | ||
metricRegistry.registerAll(TimeLimiterMetrics.ofTimeLimiter(timeLimiter)); | ||
Supplier<CompletableFuture<String>> futureSupplier = () -> | ||
CompletableFuture.supplyAsync(this::fail); | ||
|
||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
|
||
assertThat(metricRegistry).hasMetricsSize(3); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + SUCCESSFUL) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + FAILED) | ||
.hasValue(2L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + TIMEOUT) | ||
.hasValue(0L); | ||
} | ||
|
||
@Test | ||
public void shouldRecordTimeouts() { | ||
TimeLimiter timeLimiter = TimeLimiter.of(TimeLimiterConfig.custom() | ||
.timeoutDuration(Duration.ZERO) | ||
.build()); | ||
metricRegistry.registerAll(TimeLimiterMetrics.ofTimeLimiter(timeLimiter)); | ||
Supplier<CompletableFuture<String>> futureSupplier = () -> | ||
CompletableFuture.supplyAsync(this::fail); | ||
|
||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); | ||
|
||
assertThat(metricRegistry).hasMetricsSize(3); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + SUCCESSFUL) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + FAILED) | ||
.hasValue(0L); | ||
assertThat(metricRegistry).counter(DEFAULT_PREFIX + TIMEOUT) | ||
.hasValue(2L); | ||
} | ||
|
||
private String fail() { | ||
throw new RuntimeException(); | ||
} | ||
|
||
} |
24 changes: 24 additions & 0 deletions
24
...ience4j-metrics/src/test/java/io/github/resilience4j/metrics/assertion/CounterAssert.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package io.github.resilience4j.metrics.assertion; | ||
|
||
import org.assertj.core.api.AbstractAssert; | ||
import org.assertj.core.api.Assertions; | ||
|
||
import com.codahale.metrics.Counter; | ||
|
||
public class CounterAssert extends AbstractAssert<CounterAssert, Counter> { | ||
|
||
public CounterAssert(Counter actual) { | ||
super(actual, CounterAssert.class); | ||
} | ||
|
||
public static CounterAssert assertThat(Counter actual) { | ||
return new CounterAssert(actual); | ||
} | ||
|
||
public <T> CounterAssert hasValue(T expected) { | ||
isNotNull(); | ||
Assertions.assertThat(actual.getCount()) | ||
.isEqualTo(expected); | ||
return this; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...-metrics/src/test/java/io/github/resilience4j/metrics/assertion/MetricRegistryAssert.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.github.resilience4j.metrics.assertion; | ||
|
||
import org.assertj.core.api.AbstractAssert; | ||
import org.assertj.core.api.Assertions; | ||
|
||
import com.codahale.metrics.MetricRegistry; | ||
|
||
public class MetricRegistryAssert extends AbstractAssert<MetricRegistryAssert, MetricRegistry> { | ||
|
||
public MetricRegistryAssert(MetricRegistry actual) { | ||
super(actual, MetricRegistryAssert.class); | ||
} | ||
|
||
public static MetricRegistryAssert assertThat(MetricRegistry actual) { | ||
return new MetricRegistryAssert(actual); | ||
} | ||
|
||
public MetricRegistryAssert hasMetricsSize(int size) { | ||
isNotNull(); | ||
Assertions.assertThat(actual.getMetrics()) | ||
.hasSize(size); | ||
return this; | ||
} | ||
|
||
public CounterAssert counter(String name) { | ||
isNotNull(); | ||
Assertions.assertThat(actual.getCounters()).containsKey(name); | ||
return CounterAssert.assertThat(actual.getCounters().get(name)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.