diff --git a/resilience4j-metrics/build.gradle b/resilience4j-metrics/build.gradle index 5af9fece0e..75da757426 100644 --- a/resilience4j-metrics/build.gradle +++ b/resilience4j-metrics/build.gradle @@ -1,4 +1,6 @@ dependencies { compile (libraries.metrics) + compileOnly project(':resilience4j-circuitbreaker') testCompile project(':resilience4j-test') + testCompile project(':resilience4j-circuitbreaker') } \ No newline at end of file diff --git a/resilience4j-metrics/src/main/java/io/github/resilience4j/metrics/CircuitBreakerMetrics.java b/resilience4j-metrics/src/main/java/io/github/resilience4j/metrics/CircuitBreakerMetrics.java new file mode 100644 index 0000000000..e6e970cd3e --- /dev/null +++ b/resilience4j-metrics/src/main/java/io/github/resilience4j/metrics/CircuitBreakerMetrics.java @@ -0,0 +1,77 @@ +/* + * + * Copyright 2017: Robert Winkler + * + * 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 com.codahale.metrics.Gauge; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.MetricSet; + +import java.util.Map; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import javaslang.collection.Array; +import javaslang.collection.Seq; + +import static com.codahale.metrics.MetricRegistry.name; + +/** + * An adapter which exports {@link CircuitBreaker.Metrics} as Dropwizard Metrics Gauges. + */ +public class CircuitBreakerMetrics implements MetricSet{ + + private final MetricRegistry metricRegistry = new MetricRegistry(); + + private CircuitBreakerMetrics(Seq circuitBreakers){ + circuitBreakers.forEach(circuitBreaker -> { + String name = circuitBreaker.getName(); + CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); + + metricRegistry.register(name("resilience4j.circuitbreaker", name, "successful"), + (Gauge) metrics::getNumberOfSuccessfulCalls); + metricRegistry.register(name("resilience4j.circuitbreaker", name, "failed"), + (Gauge) metrics::getNumberOfFailedCalls); + metricRegistry.register(name("resilience4j.circuitbreaker", name, "not_permitted"), + (Gauge) metrics::getNumberOfNotPermittedCalls); + metricRegistry.register(name("resilience4j.circuitbreaker", name, "buffered"), + (Gauge) metrics::getNumberOfBufferedCalls); + metricRegistry.register(name("resilience4j.circuitbreaker", name, "buffered_max"), + (Gauge) metrics::getMaxNumberOfBufferedCalls); + } + ); + } + + public static CircuitBreakerMetrics of(CircuitBreakerRegistry circuitBreakerRegistry) { + return new CircuitBreakerMetrics(circuitBreakerRegistry.getAllCircuitBreakers()); + } + + public static CircuitBreakerMetrics of(Seq circuitBreakers) { + return new CircuitBreakerMetrics(circuitBreakers); + } + + public static CircuitBreakerMetrics of(CircuitBreaker circuitBreaker) { + return new CircuitBreakerMetrics(Array.of(circuitBreaker)); + } + + @Override + public Map getMetrics() { + return metricRegistry.getMetrics(); + } +} diff --git a/resilience4j-metrics/src/test/java/io/github/resilience4j/metrics/CircuitBreakerMetricsTest.java b/resilience4j-metrics/src/test/java/io/github/resilience4j/metrics/CircuitBreakerMetricsTest.java new file mode 100644 index 0000000000..0770714ffb --- /dev/null +++ b/resilience4j-metrics/src/test/java/io/github/resilience4j/metrics/CircuitBreakerMetricsTest.java @@ -0,0 +1,71 @@ +/* + * + * Copyright 2017: Robert Winkler + * + * 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 com.codahale.metrics.MetricRegistry; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.BDDMockito; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.test.HelloWorldService; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; + +public class CircuitBreakerMetricsTest { + + private MetricRegistry metricRegistry; + private HelloWorldService helloWorldService; + + @Before + public void setUp(){ + metricRegistry = new MetricRegistry(); + helloWorldService = mock(HelloWorldService.class); + } + + @Test + public void shouldRegisterMetrics() throws Throwable { + //Given + CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults(); + CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("testName"); + metricRegistry.registerAll(CircuitBreakerMetrics.of(circuitBreakerRegistry)); + + // Given the HelloWorldService returns Hello world + BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); + + //When + String value = circuitBreaker.executeSupplier(helloWorldService::returnHelloWorld); + + //Then + assertThat(value).isEqualTo("Hello world"); + // Then the helloWorldService should be invoked 1 time + BDDMockito.then(helloWorldService).should(times(1)).returnHelloWorld(); + assertThat(metricRegistry.getMetrics()).hasSize(5); + assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.buffered").getValue()).isEqualTo(1); + assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.successful").getValue()).isEqualTo(1); + assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.failed").getValue()).isEqualTo(0); + assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.not_permitted").getValue()).isEqualTo(0L); + assertThat(metricRegistry.getGauges().get("resilience4j.circuitbreaker.testName.buffered_max").getValue()).isEqualTo(100); + + } +} diff --git a/resilience4j-prometheus/build.gradle b/resilience4j-prometheus/build.gradle index 33c0abb959..6538de6e85 100644 --- a/resilience4j-prometheus/build.gradle +++ b/resilience4j-prometheus/build.gradle @@ -1,5 +1,7 @@ dependencies { compile (libraries.prometheus_simpleclient) - compile project(':resilience4j-circuitbreaker') - compile project(':resilience4j-ratelimiter') + compileOnly project(':resilience4j-circuitbreaker') + compileOnly project(':resilience4j-ratelimiter') + testCompile project(':resilience4j-circuitbreaker') + testCompile project(':resilience4j-ratelimiter') } diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java index cb5d845786..aa0d312055 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java @@ -39,8 +39,13 @@ public class CircuitBreakerAutoConfiguration { @Bean - public CircuitBreakerRegistry circuitBreakerRegistry(){ - return new InMemoryCircuitBreakerRegistry(); + public CircuitBreakerRegistry circuitBreakerRegistry(CircuitBreakerProperties circuitBreakerProperties){ + CircuitBreakerRegistry circuitBreakerRegistry = new InMemoryCircuitBreakerRegistry(); + circuitBreakerProperties.getBackends().forEach( + (name, properties) -> circuitBreakerRegistry.circuitBreaker(name, circuitBreakerProperties + .createCircuitBreakerConfig(name)) + ); + return circuitBreakerRegistry; } @Bean