Skip to content

Commit

Permalink
Stork observability integration
Browse files Browse the repository at this point in the history
  • Loading branch information
aureamunoz committed Sep 22, 2023
1 parent e6ea6d5 commit dd7f3c1
Show file tree
Hide file tree
Showing 23 changed files with 901 additions and 7 deletions.
2 changes: 1 addition & 1 deletion bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
<smallrye-reactive-types-converter.version>3.0.1</smallrye-reactive-types-converter.version>
<smallrye-mutiny-vertx-binding.version>3.6.0</smallrye-mutiny-vertx-binding.version>
<smallrye-reactive-messaging.version>4.9.0</smallrye-reactive-messaging.version>
<smallrye-stork.version>2.3.1</smallrye-stork.version>
<smallrye-stork.version>2.4.0-SNAPSHOT</smallrye-stork.version>
<jakarta.activation.version>2.1.2</jakarta.activation.version>
<jakarta.annotation-api.version>2.1.1</jakarta.annotation-api.version>
<jakarta.authentication-api>3.0.0</jakarta.authentication-api>
Expand Down
22 changes: 18 additions & 4 deletions extensions/micrometer/deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,26 @@

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-deployment</artifactId>
<artifactId>quarkus-resteasy-reactive-deployment</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-deployment</artifactId>
<artifactId>quarkus-rest-client-reactive-deployment</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson-deployment</artifactId>
<artifactId>quarkus-resteasy-reactive-jackson-deployment</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.stork</groupId>
<artifactId>stork-service-discovery-static-list</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-undertow-deployment</artifactId>
Expand Down Expand Up @@ -132,6 +136,16 @@
<artifactId>resteasy-reactive-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.quarkus.micrometer.deployment.binder;

import java.util.function.BooleanSupplier;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.micrometer.runtime.MicrometerRecorder;
import io.quarkus.micrometer.runtime.config.MicrometerConfig;

public class StorkBinderProcessor {

static final String OBSERVABLE_CLIENT = "io.smallrye.stork.api.Service";
static final String METRICS_BEAN_CLASS = "io.quarkus.micrometer.runtime.binder.stork.StorkObservationCollectorBean";

static final Class<?> OBSERVABLE_CLIENT_CLASS = MicrometerRecorder.getClassForName(OBSERVABLE_CLIENT);

static class StorkMetricsSupportEnabled implements BooleanSupplier {
MicrometerConfig mConfig;

public boolean getAsBoolean() {
return OBSERVABLE_CLIENT_CLASS != null && mConfig.checkBinderEnabledWithDefault(mConfig.binder.stork);
}
}

@BuildStep(onlyIf = StorkMetricsSupportEnabled.class)
AdditionalBeanBuildItem addStorkObservationCollector() {
return AdditionalBeanBuildItem.unremovableOf(METRICS_BEAN_CLASS);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.micrometer.deployment.binder;

import static org.junit.jupiter.api.Assertions.assertTrue;

import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.smallrye.stork.api.observability.ObservationCollector;

public class StorkMetricsDisabledTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("test-logging.properties")
.overrideConfigKey("quarkus.micrometer.binder.stork.enabled", "false")
.overrideConfigKey("quarkus.micrometer.binder-enabled-default", "false")
.overrideConfigKey("quarkus.micrometer.registry-enabled-default", "false")
.withEmptyApplication();

@Inject
Instance<ObservationCollector> bean;

@Test
void testNoInstancePresentIfNoRedisClientsClass() {
assertTrue(bean.isUnsatisfied(),
"No redis metrics bean");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@

package io.quarkus.micrometer.deployment.binder;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import java.util.concurrent.TimeUnit;

import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.quarkus.micrometer.runtime.binder.stork.StorkObservationCollectorBean;
import io.quarkus.micrometer.test.MockServiceSelectorConfiguration;
import io.quarkus.micrometer.test.MockServiceSelectorProvider;
import io.quarkus.micrometer.test.MockServiceSelectorProviderLoader;
import io.quarkus.micrometer.test.PingPongResource;
import io.quarkus.micrometer.test.Util;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.smallrye.stork.api.observability.ObservationPoints;

@DisabledOnOs(OS.WINDOWS)
public class StorkMetricsLoadBalancerFailTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("test-logging.properties")
.overrideConfigKey("quarkus.micrometer.binder.stork.enabled", "true")
.overrideConfigKey("quarkus.stork.pingpong-service.service-discovery.type", "static")
.overrideConfigKey("quarkus.stork.pingpong-service.service-discovery.address-list", "${test.url}")
.overrideConfigKey("pingpong/mp-rest/url", "stork://pingpong-service")
.overrideConfigKey("quarkus.stork.pingpong-service.load-balancer.type", "mock")
.withApplicationRoot((jar) -> jar
.addClasses(PingPongResource.class, PingPongResource.PingPongRestClient.class,
MockServiceSelectorProvider.class, MockServiceSelectorConfiguration.class,
MockServiceSelectorProviderLoader.class, Util.class));

@Inject
MeterRegistry registry;

@Inject
MockServiceSelectorProvider provider;

@Test
public void shouldGetStorkMetricsWhenServiceSelectorFails() {

Mockito.when(provider.getLoadBalancer().selectServiceInstance(Mockito.anyCollection()))
.thenThrow(new RuntimeException("Load Balancer induced failure"));
RestAssured.when().get("/ping/one").then().statusCode(500);

//Stork metrics
assertStorkMetrics();

// Stork metrics exposed to Micrometer
Counter instanceCounter = registry.get("stork.instances.count").counter();
Timer serviceDiscoveryDuration = registry.get("stork.service-discovery.duration").timer();
Timer serviceSelectionDuration = registry.get("stork.service-selection.duration").timer();
Timer overallDuration = registry.get("stork.overall.duration").timer();
Gauge serviceDiscoveryFailures = registry.get("stork.service-discovery.failures").gauge();
Gauge loadBalancerFailures = registry.get("stork.load-balancer.failures").gauge();

Util.assertTags(Tag.of("service-name", "pingpong-service"), instanceCounter, serviceDiscoveryDuration,
serviceSelectionDuration, overallDuration);

assertThat(StorkObservationCollectorBean.STORK_METRICS.failure()).isNotNull();
assertThat(instanceCounter.count()).isEqualTo(1);
assertThat(serviceDiscoveryFailures.value()).isEqualTo(0);
assertThat(loadBalancerFailures.value()).isEqualTo(1);
assertThat(serviceDiscoveryDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);
assertThat(serviceSelectionDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);
assertThat(overallDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);

}

private static void assertStorkMetrics() {
ObservationPoints.StorkResolutionEvent metrics = StorkObservationCollectorBean.STORK_METRICS;
Assertions.assertThat(metrics.getDiscoveredInstancesCount()).isEqualTo(1);
Assertions.assertThat(metrics.getSelectedInstanceId()).isEqualTo(-1);
Assertions.assertThat(metrics.getServiceName()).isEqualTo("pingpong-service");
Assertions.assertThat(metrics.isDone()).isTrue();
Assertions.assertThat(metrics.failure().getMessage()).isEqualTo("Load Balancer induced failure");
Assertions.assertThat(metrics.getOverallDuration()).isNotNull();
Assertions.assertThat(metrics.getServiceDiscoveryType()).isEqualTo("static");
Assertions.assertThat(metrics.getServiceSelectionType()).isEqualTo("mock");
Assertions.assertThat(metrics.getServiceDiscoveryDuration()).isNotNull();
Assertions.assertThat(metrics.getServiceSelectionDuration()).isNotNull();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@

package io.quarkus.micrometer.deployment.binder;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;

import java.util.concurrent.TimeUnit;

import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import io.quarkus.micrometer.runtime.binder.stork.StorkObservationCollectorBean;
import io.quarkus.micrometer.test.MockServiceDiscoveryConfiguration;
import io.quarkus.micrometer.test.MockServiceDiscoveryProvider;
import io.quarkus.micrometer.test.MockServiceDiscoveryProviderLoader;
import io.quarkus.micrometer.test.PingPongResource;
import io.quarkus.micrometer.test.Util;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.smallrye.mutiny.Uni;
import io.smallrye.stork.api.observability.ObservationPoints;

@DisabledOnOs(OS.WINDOWS)
public class StorkMetricsServiceDiscoveryFailTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withConfigurationResource("test-logging.properties")
.overrideConfigKey("quarkus.micrometer.binder.stork.enabled", "true")
.overrideConfigKey("quarkus.stork.pingpong-service.service-discovery.type", "mock")
.overrideConfigKey("pingpong/mp-rest/url", "stork://pingpong-service")
.withApplicationRoot((jar) -> jar
.addClasses(PingPongResource.class, PingPongResource.PingPongRestClient.class,
MockServiceDiscoveryProvider.class, MockServiceDiscoveryConfiguration.class,
MockServiceDiscoveryProviderLoader.class, Util.class));

@Inject
MeterRegistry registry;

@Inject
MockServiceDiscoveryProvider provider;

@Test
public void shouldGetStorkMetricsWhenServiceDiscoveryFails() {

Mockito.when(provider.getServiceDiscovery().getServiceInstances())
.thenReturn(Uni.createFrom().failure(new RuntimeException("Service Discovery induced failure")));
RestAssured.when().get("/ping/one").then().statusCode(500);

//Stork metrics
assertStorkMetrics();

// Stork metrics exposed to Micrometer
Counter instanceCounter = registry.get("stork.instances.count").counter();
Timer serviceDiscoveryDuration = registry.get("stork.service-discovery.duration").timer();
Timer serviceSelectionDuration = registry.get("stork.service-selection.duration").timer();
Timer overallDuration = registry.get("stork.overall.duration").timer();
Gauge serviceDiscoveryFailures = registry.get("stork.service-discovery.failures").gauge();
Gauge loadBalancerFailures = registry.get("stork.load-balancer.failures").gauge();

Util.assertTags(Tag.of("service-name", "pingpong-service"), instanceCounter, serviceDiscoveryDuration,
serviceSelectionDuration, overallDuration);

assertThat(StorkObservationCollectorBean.STORK_METRICS.failure()).isNotNull();
assertThat(instanceCounter.count()).isEqualTo(0);
assertThat(serviceDiscoveryFailures.value()).isEqualTo(1);
assertThat(loadBalancerFailures.value()).isEqualTo(0);
assertThat(serviceDiscoveryDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);
assertThat(serviceSelectionDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);
assertThat(overallDuration.totalTime(TimeUnit.NANOSECONDS)).isGreaterThan(0);

}

private static void assertStorkMetrics() {
ObservationPoints.StorkResolutionEvent metrics = StorkObservationCollectorBean.STORK_METRICS;
Assertions.assertThat(metrics.getDiscoveredInstancesCount()).isEqualTo(0);
Assertions.assertThat(metrics.getSelectedInstanceId()).isEqualTo(-1);
Assertions.assertThat(metrics.getServiceName()).isEqualTo("pingpong-service");
Assertions.assertThat(metrics.isDone()).isTrue();
Assertions.assertThat(metrics.failure().getMessage()).isEqualTo("Service Discovery induced failure");
Assertions.assertThat(metrics.getOverallDuration()).isNotNull();
Assertions.assertThat(metrics.getServiceDiscoveryType()).isEqualTo("mock");
Assertions.assertThat(metrics.getServiceSelectionType()).isEqualTo("round-robin");
Assertions.assertThat(metrics.getServiceDiscoveryDuration()).isNotNull();
Assertions.assertThat(metrics.getServiceSelectionDuration()).isNotNull();
}

}
Loading

0 comments on commit dd7f3c1

Please sign in to comment.