From 5b678460183becbf4fac430d7284f1f38b97ef3e Mon Sep 17 00:00:00 2001 From: Ales Justin Date: Fri, 17 Jan 2025 11:08:41 +0100 Subject: [PATCH 1/2] Improvements to the Grafana LGTM dashboards --- .../common/ContainerConstants.java | 8 ++ .../common/config/ContainerConfigUtil.java | 41 +++++- .../common/config/LgtmConfig.java | 36 +++++ .../common/config/OverrideProperty.java | 21 +++ .../ObservabilityDevServiceProcessor.java | 11 ++ .../testcontainers/LgtmContainer.java | 67 +++++++-- .../devresource/ExtensionsCatalog.java | 7 +- .../devresource/lgtm/LgtmResource.java | 43 +++++- .../observability-lgtm-prometheus/pom.xml | 127 ++++++++++++++++++ .../observability/example/SimpleEndpoint.java | 48 +++++++ .../src/main/resources/application.properties | 4 + .../quarkus/observability/test/LgtmTest.java | 46 +++++++ .../src/test/resources/application.properties | 8 ++ integration-tests/observability-lgtm/pom.xml | 6 + .../observability/test/LgtmReloadTest.java | 2 +- .../observability/test/LgtmTestHelper.java | 2 +- integration-tests/pom.xml | 1 + test-framework/observability/pom.xml | 22 +++ .../test/utils}/GrafanaClient.java | 2 +- .../test/utils}/QueryResult.java | 2 +- .../test/utils}/TempoResult.java | 2 +- .../observability/test/utils}/User.java | 2 +- test-framework/pom.xml | 1 + 23 files changed, 483 insertions(+), 26 deletions(-) create mode 100644 extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/OverrideProperty.java create mode 100644 integration-tests/observability-lgtm-prometheus/pom.xml create mode 100644 integration-tests/observability-lgtm-prometheus/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java create mode 100644 integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties create mode 100644 integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java create mode 100644 integration-tests/observability-lgtm-prometheus/src/test/resources/application.properties create mode 100644 test-framework/observability/pom.xml rename {integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support => test-framework/observability/src/main/java/io/quarkus/observability/test/utils}/GrafanaClient.java (99%) rename {integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support => test-framework/observability/src/main/java/io/quarkus/observability/test/utils}/QueryResult.java (97%) rename {integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support => test-framework/observability/src/main/java/io/quarkus/observability/test/utils}/TempoResult.java (95%) rename {integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support => test-framework/observability/src/main/java/io/quarkus/observability/test/utils}/User.java (86%) diff --git a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java index 55576ccd82b1e..489378273d323 100644 --- a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java +++ b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java @@ -15,4 +15,12 @@ public final class ContainerConstants { public static final String OTEL_GRPC_PROTOCOL = "grpc"; public static final String OTEL_HTTP_PROTOCOL = "http/protobuf"; + + // Overrides + + public static final int SCRAPING_INTERVAL = 10; + public static final String OTEL_METRIC_EXPORT_INTERVAL = "10s"; + public static final String OTEL_BSP_SCHEDULE_DELAY = "3s"; + public static final String OTEL_BLRP_SCHEDULE_DELAY = "1s"; + } diff --git a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/ContainerConfigUtil.java b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/ContainerConfigUtil.java index 8d3ce6d1a0b42..2f468afd12b50 100644 --- a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/ContainerConfigUtil.java +++ b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/ContainerConfigUtil.java @@ -2,7 +2,10 @@ import java.lang.reflect.Method; import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.Optional; public class ContainerConfigUtil { /** @@ -16,11 +19,7 @@ public static boolean isEqual(ContainerConfig cc1, ContainerConfig cc2) { return false; } - Class i = Arrays.stream(c1.getInterfaces()) - .filter(ContainerConfig.class::isAssignableFrom) - .findFirst() - .orElseThrow(() -> new IllegalArgumentException("Missing ContainerConfig based interface")); - Method[] methods = i.getMethods(); // should get all config methods + Method[] methods = getMethods(c1); for (Method m : methods) { Object v1 = invoke(m, cc1); Object v2 = invoke(m, cc2); @@ -31,6 +30,38 @@ public static boolean isEqual(ContainerConfig cc1, ContainerConfig cc2) { return true; } + /** + * Get all properties to override from container config instance. + * + * @param config the container config + * @return map of properties to override + */ + public static Map propertiesToOverride(ContainerConfig config) { + Map map = new HashMap<>(); + for (Method m : getMethods(config.getClass())) { + OverrideProperty override = m.getAnnotation(OverrideProperty.class); + if (override != null) { + String key = override.value(); + Object value = invoke(m, config); + if (value instanceof Optional) { + Optional optional = (Optional) value; + optional.ifPresent(o -> map.put(key, o)); + } else if (value != null) { + map.put(key, value); + } + } + } + return map; + } + + private static Method[] getMethods(Class c1) { + Class i = Arrays.stream(c1.getInterfaces()) + .filter(ContainerConfig.class::isAssignableFrom) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Missing ContainerConfig based interface")); + return i.getMethods(); + } + private static Object invoke(Method m, Object target) { try { return m.invoke(target); diff --git a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/LgtmConfig.java b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/LgtmConfig.java index 227948a7938a1..62270762f6dbd 100644 --- a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/LgtmConfig.java +++ b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/LgtmConfig.java @@ -4,6 +4,7 @@ import java.util.Set; import io.quarkus.observability.common.ContainerConstants; +import io.quarkus.runtime.annotations.ConfigDocIgnore; import io.quarkus.runtime.annotations.ConfigGroup; import io.smallrye.config.WithDefault; @@ -37,4 +38,39 @@ public interface LgtmConfig extends GrafanaConfig { */ @WithDefault(ContainerConstants.OTEL_HTTP_PROTOCOL) String otlpProtocol(); + + /** + * The (Prometheus) scraping interval, in seconds. + */ + @WithDefault(ContainerConstants.SCRAPING_INTERVAL + "") + int scrapingInterval(); + + /** + * Do we force scraping. + */ + Optional forceScraping(); + + /** + * A way to override `quarkus.otel.metric.export.interval` property's default value. + */ + @OverrideProperty("quarkus.otel.metric.export.interval") + @WithDefault(ContainerConstants.OTEL_METRIC_EXPORT_INTERVAL) + @ConfigDocIgnore + String otelMetricExportInterval(); + + /** + * A way to override `quarkus.otel.bsp.schedule.delay` property's default value. + */ + @OverrideProperty("quarkus.otel.bsp.schedule.delay") + @WithDefault(ContainerConstants.OTEL_BSP_SCHEDULE_DELAY) + @ConfigDocIgnore + String otelBspScheduleDelay(); + + /** + * A way to override `quarkus.otel.metric.export.interval` property's default value. + */ + @OverrideProperty("quarkus.otel.blrp.schedule.delay") + @WithDefault(ContainerConstants.OTEL_BLRP_SCHEDULE_DELAY) + @ConfigDocIgnore + String otelBlrpScheduleDelay(); } diff --git a/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/OverrideProperty.java b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/OverrideProperty.java new file mode 100644 index 0000000000000..67d094fae29e8 --- /dev/null +++ b/extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/OverrideProperty.java @@ -0,0 +1,21 @@ +package io.quarkus.observability.common.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Override the property in the value, + * with the value of the annotated method's return. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD }) +public @interface OverrideProperty { + /** + * The property key to override. + * + * @return the property key + */ + String value(); +} diff --git a/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java b/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java index d2093a51decc1..655862baa9dae 100644 --- a/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java +++ b/extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java @@ -28,6 +28,7 @@ import io.quarkus.deployment.builditem.DockerStatusBuildItem; import io.quarkus.deployment.builditem.FeatureBuildItem; import io.quarkus.deployment.builditem.LaunchModeBuildItem; +import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem; import io.quarkus.deployment.console.ConsoleInstalledBuildItem; import io.quarkus.deployment.console.StartupLogCompressor; import io.quarkus.deployment.dev.devservices.DevServicesConfig; @@ -84,6 +85,7 @@ public void startContainers(LaunchModeBuildItem launchMode, LoggingSetupBuildItem loggingSetupBuildItem, DevServicesConfig devServicesConfig, BuildProducer services, + BuildProducer properties, Capabilities capabilities, Optional metricsConfiguration, BuildProducer configBuildProducer) { @@ -118,6 +120,8 @@ public void startContainers(LaunchModeBuildItem launchMode, ContainerConfig currentDevServicesConfiguration = dev.config( configuration, new ExtensionsCatalog( + QuarkusClassLoader::isResourcePresentAtRuntime, + QuarkusClassLoader::isClassPresentAtRuntime, capabilities.isPresent(Capability.OPENTELEMETRY_TRACER), hasMicrometerOtlp(metricsConfiguration))); @@ -140,6 +144,13 @@ public void startContainers(LaunchModeBuildItem launchMode, devServices.remove(devId); // clean-up capturedDevServicesConfigurations.put(devId, currentDevServicesConfiguration); + // override some OTel, etc defaults - rates, intervals, delays, ... + Map propertiesToOverride = ContainerConfigUtil + .propertiesToOverride(currentDevServicesConfiguration); + propertiesToOverride + .forEach((k, v) -> properties.produce(new RunTimeConfigurationDefaultBuildItem(k, v.toString()))); + log.infof("Dev Service %s properties override: %s", devId, propertiesToOverride); + StartupLogCompressor compressor = new StartupLogCompressor( (launchMode.isTest() ? "(test) " : "") + devId + " Dev Services Starting:", consoleInstalledBuildItem, diff --git a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java index eeb284a767ecd..2d577b3131bbc 100644 --- a/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java +++ b/extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java @@ -15,11 +15,12 @@ import io.quarkus.observability.common.ContainerConstants; import io.quarkus.observability.common.config.AbstractGrafanaConfig; import io.quarkus.observability.common.config.LgtmConfig; +import io.quarkus.runtime.LaunchMode; public class LgtmContainer extends GrafanaContainer { protected static final String LGTM_NETWORK_ALIAS = "ltgm.testcontainer.docker"; - protected static final String PROMETHEUS_CONFIG = """ + protected static final String PROMETHEUS_CONFIG_DEFAULT = """ --- otlp: # Recommended attributes to be promoted to labels. @@ -47,12 +48,15 @@ public class LgtmContainer extends GrafanaContainer { # A 10min time window is enough because it can easily absorb retries and network delays. out_of_order_time_window: 10m global: - scrape_interval: 5s + scrape_interval: %s evaluation_interval: 5s + """; + + protected static final String PROMETHEUS_CONFIG_SCRAPE = """ scrape_configs: - job_name: '%s' metrics_path: '%s%s' - scrape_interval: 5s + scrape_interval: %s static_configs: - targets: ['%s:%d'] """; @@ -83,12 +87,16 @@ public class LgtmContainer extends GrafanaContainer { foldersFromFilesStructure: false """; - public LgtmContainer() { - this(new LgtmConfigImpl()); + private final boolean scrapingRequired; + + public LgtmContainer(boolean scrapingRequired) { + this(new LgtmConfigImpl(), scrapingRequired); } - public LgtmContainer(LgtmConfig config) { + public LgtmContainer(LgtmConfig config, boolean scrapingRequired) { super(config); + // do we require scraping + this.scrapingRequired = scrapingRequired; // always expose both -- since the LGTM image already does that as well addExposedPorts(ContainerConstants.OTEL_GRPC_EXPORTER_PORT, ContainerConstants.OTEL_HTTP_EXPORTER_PORT); @@ -157,12 +165,22 @@ public static int getPrivateOtlpPort(String otlpProtocol) { } private String getPrometheusConfig() { - Config runtimeConfig = ConfigProvider.getConfig(); - String rootPath = runtimeConfig.getOptionalValue("quarkus.management.root-path", String.class).orElse("/q"); - String metricsPath = runtimeConfig.getOptionalValue("quarkus.management.metrics.path", String.class).orElse("/metrics"); - int httpPort = runtimeConfig.getOptionalValue("quarkus.http.port", Integer.class).orElse(8080); // when not set use default - - return String.format(PROMETHEUS_CONFIG, config.serviceName(), rootPath, metricsPath, "host.docker.internal", httpPort); + String scraping = config.scrapingInterval() + "s"; + String prometheusConfig = String.format(PROMETHEUS_CONFIG_DEFAULT, scraping); + if (config.forceScraping().orElse(scrapingRequired)) { + boolean isTest = LaunchMode.current() == LaunchMode.TEST; + Config runtimeConfig = ConfigProvider.getConfig(); + String rootPath = runtimeConfig.getOptionalValue("quarkus.management.root-path", String.class).orElse("/q"); + String metricsPath = runtimeConfig.getOptionalValue("quarkus.management.metrics.path", String.class) + .orElse("/metrics"); + String httpPortKey = isTest ? "quarkus.http.test-port" : "quarkus.http.port"; + Optional optionalValue = runtimeConfig.getOptionalValue(httpPortKey, Integer.class); + int httpPort = optionalValue.orElse(isTest ? 8081 : 8080); // when not set use default + + prometheusConfig += String.format(PROMETHEUS_CONFIG_SCRAPE, config.serviceName(), rootPath, metricsPath, scraping, + "host.docker.internal", httpPort); + } + return prometheusConfig; } protected static class LgtmConfigImpl extends AbstractGrafanaConfig implements LgtmConfig { @@ -183,6 +201,31 @@ public Optional> networkAliases() { public String otlpProtocol() { return ContainerConstants.OTEL_HTTP_PROTOCOL; } + + @Override + public int scrapingInterval() { + return ContainerConstants.SCRAPING_INTERVAL; + } + + @Override + public Optional forceScraping() { + return Optional.empty(); + } + + @Override + public String otelMetricExportInterval() { + return ContainerConstants.OTEL_METRIC_EXPORT_INTERVAL; + } + + @Override + public String otelBspScheduleDelay() { + return ContainerConstants.OTEL_BSP_SCHEDULE_DELAY; + } + + @Override + public String otelBlrpScheduleDelay() { + return ContainerConstants.OTEL_BLRP_SCHEDULE_DELAY; + } } protected static class LgtmLoggingFilter implements Predicate { diff --git a/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java index 00af2105c6c0b..2a2ccadb367cd 100644 --- a/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java +++ b/extensions/observability-devservices/testlibs/devresource-common/src/main/java/io/quarkus/observability/devresource/ExtensionsCatalog.java @@ -1,8 +1,13 @@ package io.quarkus.observability.devresource; +import java.util.function.Function; + /** * Relevant Observability extensions present. */ -public record ExtensionsCatalog(boolean hasOpenTelemetry, +public record ExtensionsCatalog( + Function resourceChecker, + Function classChecker, + boolean hasOpenTelemetry, boolean hasMicrometerOtlp) { } diff --git a/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java b/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java index ba383cfde699d..b41947d4791a5 100644 --- a/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java +++ b/extensions/observability-devservices/testlibs/devresource-lgtm/src/main/java/io/quarkus/observability/devresource/lgtm/LgtmResource.java @@ -2,6 +2,10 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import org.jboss.logging.Logger; import io.quarkus.observability.common.ContainerConstants; import io.quarkus.observability.common.config.LgtmConfig; @@ -14,6 +18,22 @@ public class LgtmResource extends ContainerResource { + private static final Logger log = Logger.getLogger(LgtmResource.class.getName()); + + protected static final Set SCRAPING_REGISTRIES = Set.of( + "io.micrometer.prometheus.PrometheusMeterRegistry"); + + protected static final Function TCCL_FN = s -> { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + cl.loadClass(s); + return true; + } catch (Exception e) { + // any exception + return false; + } + }; + private ExtensionsCatalog catalog; private LgtmConfig config; @@ -32,7 +52,26 @@ public LgtmConfig config(ModulesConfiguration configuration, ExtensionsCatalog c @Override public Container container(LgtmConfig config, ModulesConfiguration root) { - return set(new LgtmContainer(config)); + return set(new LgtmContainer(config, isScrapingRequired(catalog.classChecker()))); + } + + private boolean isScrapingRequired(Function checker) { + boolean result = false; + String foundRegistry = null; + for (String clazz : SCRAPING_REGISTRIES) { + if (checker.apply(clazz)) { + foundRegistry = clazz; + result = true; + break; + } + } + + if (result && (catalog != null && catalog.hasMicrometerOtlp())) { + log.warnf("Multiple Micrometer registries found - OTLP and %s, no Prometheus scrapping required.", foundRegistry); + return false; + } + + return result; } private int getPrivateOtlpPort() { @@ -86,7 +125,7 @@ public Map config(int privatePort, String host, int publicPort) @Override protected LgtmContainer defaultContainer() { - return new LgtmContainer(); + return new LgtmContainer(isScrapingRequired(TCCL_FN)); // best we can do? } @Override diff --git a/integration-tests/observability-lgtm-prometheus/pom.xml b/integration-tests/observability-lgtm-prometheus/pom.xml new file mode 100644 index 0000000000000..c424451aa2ffd --- /dev/null +++ b/integration-tests/observability-lgtm-prometheus/pom.xml @@ -0,0 +1,127 @@ + + + 4.0.0 + + + quarkus-integration-tests-parent + io.quarkus + 999-SNAPSHOT + + + quarkus-integration-test-observability-lgtm-prometheus + Quarkus - Integration Tests - Observability LGTM Prometheus + + + + io.quarkus + quarkus-observability-devservices-lgtm + + + io.quarkus + quarkus-rest + + + + io.quarkus + quarkus-micrometer-registry-prometheus + + + io.quarkus + quarkus-opentelemetry + + + com.fasterxml.jackson.core + jackson-databind + + + io.quarkus + quarkus-junit5 + test + + + io.quarkus + quarkus-junit5-internal + test + + + io.rest-assured + rest-assured + test + + + org.awaitility + awaitility + test + + + org.assertj + assertj-core + test + + + io.quarkus + quarkus-test-observability + ${project.version} + test + + + + + io.quarkus + quarkus-rest-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-opentelemetry-deployment + ${project.version} + pom + test + + + * + * + + + + + io.quarkus + quarkus-micrometer-registry-prometheus-deployment + ${project.version} + pom + test + + + * + * + + + + + + + + + io.quarkus + quarkus-maven-plugin + + + + build + + + + + + + diff --git a/integration-tests/observability-lgtm-prometheus/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java b/integration-tests/observability-lgtm-prometheus/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java new file mode 100644 index 0000000000000..17684efd1e944 --- /dev/null +++ b/integration-tests/observability-lgtm-prometheus/src/main/java/io/quarkus/observability/example/SimpleEndpoint.java @@ -0,0 +1,48 @@ +package io.quarkus.observability.example; + +import java.security.SecureRandom; +import java.util.Random; + +import jakarta.annotation.PostConstruct; +import jakarta.inject.Inject; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.MediaType; + +import org.jboss.logging.Logger; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; + +@Path("/api") +public class SimpleEndpoint { + private static final Logger log = Logger.getLogger(SimpleEndpoint.class); + + @Inject + MeterRegistry registry; + + Random random = new SecureRandom(); + double[] arr = new double[1]; + + @PostConstruct + public void start() { + String key = System.getProperty("tag-key", "test"); + Gauge.builder("xvalue", arr, a -> arr[0]) + .baseUnit("X") + .description("Some random x") + .tag(key, "x") + .register(registry); + } + + @GET + @Produces(MediaType.TEXT_PLAIN) + @Path("/poke") + public String poke(@QueryParam("f") int f) { + log.infof("Poke %s", f); + double x = random.nextDouble() * f; + arr[0] = x; + return "poke:" + x; + } +} diff --git a/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties b/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties new file mode 100644 index 0000000000000..f7dcd93e1974e --- /dev/null +++ b/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties @@ -0,0 +1,4 @@ +quarkus.log.category."io.quarkus.observability".level=DEBUG +quarkus.log.category."io.quarkus.devservices".level=DEBUG + +quarkus.micrometer.binder-enabled-default=false diff --git a/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java b/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java new file mode 100644 index 0000000000000..e57206abf14de --- /dev/null +++ b/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java @@ -0,0 +1,46 @@ +package io.quarkus.observability.test; + +import java.util.concurrent.TimeUnit; + +import org.awaitility.Awaitility; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.jboss.logging.Logger; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import io.quarkus.observability.test.utils.GrafanaClient; +import io.quarkus.test.junit.QuarkusTest; +import io.restassured.RestAssured; + +/** + * Duplicated test, similar to observability-lgtm. + * The difference is dependencies / classpath, + * where this one has / uses Prometheus Micrometer registry. + */ +@QuarkusTest +@DisabledOnOs(OS.WINDOWS) +public class LgtmTest { + protected final Logger log = Logger.getLogger(getClass()); + + @ConfigProperty(name = "grafana.endpoint") + String endpoint; + + @Test + public void testPoke() { + log.info("Testing Grafana ..."); + String response = RestAssured.get("/api/poke?f=100").body().asString(); + log.info("Response: " + response); + GrafanaClient client = new GrafanaClient(endpoint, "admin", "admin"); + Awaitility.await().atMost(61, TimeUnit.SECONDS).until( + client::user, + u -> "admin".equals(u.login)); + Awaitility.await().atMost(61, TimeUnit.SECONDS).until( + () -> client.query("xvalue_X"), + result -> !result.data.result.isEmpty()); + Awaitility.await().atMost(61, TimeUnit.SECONDS).until( + () -> client.traces("quarkus-integration-test-observability-lgtm-prometheus", 20, 3), + result -> !result.traces.isEmpty()); + } + +} diff --git a/integration-tests/observability-lgtm-prometheus/src/test/resources/application.properties b/integration-tests/observability-lgtm-prometheus/src/test/resources/application.properties new file mode 100644 index 0000000000000..58c3725bfe3f7 --- /dev/null +++ b/integration-tests/observability-lgtm-prometheus/src/test/resources/application.properties @@ -0,0 +1,8 @@ +# Disable default binders +quarkus.micrometer.binder-enabled-default=false + +quarkus.log.category."io.quarkus.observability".level=DEBUG +quarkus.log.category."io.quarkus.devservices".level=DEBUG + +quarkus.observability.lgtm.timeout=PT3M +quarkus.observability.lgtm.scraping-interval=2 \ No newline at end of file diff --git a/integration-tests/observability-lgtm/pom.xml b/integration-tests/observability-lgtm/pom.xml index c089bb3ac0fcf..86e2b268369a4 100644 --- a/integration-tests/observability-lgtm/pom.xml +++ b/integration-tests/observability-lgtm/pom.xml @@ -61,6 +61,12 @@ assertj-core test + + io.quarkus + quarkus-test-observability + ${project.version} + test + diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java index 58eaba5abbfac..e6a2305641d3d 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmReloadTest.java @@ -7,8 +7,8 @@ import org.junit.jupiter.api.extension.RegisterExtension; import io.quarkus.observability.test.support.ConfigEndpoint; -import io.quarkus.observability.test.support.GrafanaClient; import io.quarkus.observability.test.support.ReloadEndpoint; +import io.quarkus.observability.test.utils.GrafanaClient; import io.quarkus.test.QuarkusDevModeTest; /** diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java index e699a105d6fff..e08f1adbafbce 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java @@ -5,7 +5,7 @@ import org.awaitility.Awaitility; import org.jboss.logging.Logger; -import io.quarkus.observability.test.support.GrafanaClient; +import io.quarkus.observability.test.utils.GrafanaClient; import io.restassured.RestAssured; public abstract class LgtmTestHelper { diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index adca102c27412..4a4caa32bbf20 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -252,6 +252,7 @@ liquibase liquibase-mongodb observability-lgtm + observability-lgtm-prometheus oidc oidc-client oidc-client-registration diff --git a/test-framework/observability/pom.xml b/test-framework/observability/pom.xml new file mode 100644 index 0000000000000..dc1399bbd4804 --- /dev/null +++ b/test-framework/observability/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + + io.quarkus + quarkus-test-framework + 999-SNAPSHOT + + + quarkus-test-observability + Quarkus - Test Framework - Observability test utils + + + + com.fasterxml.jackson.core + jackson-databind + + + diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java similarity index 99% rename from integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java rename to test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java index 91b3ed64ebc22..411b6aa92a89b 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/GrafanaClient.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java @@ -1,4 +1,4 @@ -package io.quarkus.observability.test.support; +package io.quarkus.observability.test.utils; import java.io.IOException; import java.io.UncheckedIOException; diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/QueryResult.java similarity index 97% rename from integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java rename to test-framework/observability/src/main/java/io/quarkus/observability/test/utils/QueryResult.java index 77f24fa7e5a26..d4d504430ad18 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/QueryResult.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/QueryResult.java @@ -1,4 +1,4 @@ -package io.quarkus.observability.test.support; +package io.quarkus.observability.test.utils; import java.util.List; diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/TempoResult.java similarity index 95% rename from integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java rename to test-framework/observability/src/main/java/io/quarkus/observability/test/utils/TempoResult.java index 480b04b5a8d92..4aad771e6c5d6 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/TempoResult.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/TempoResult.java @@ -1,4 +1,4 @@ -package io.quarkus.observability.test.support; +package io.quarkus.observability.test.utils; import java.util.List; import java.util.Map; diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java similarity index 86% rename from integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java rename to test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java index f617cd2b23bcf..f07c68dac8e81 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/support/User.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java @@ -1,4 +1,4 @@ -package io.quarkus.observability.test.support; +package io.quarkus.observability.test.utils; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; diff --git a/test-framework/pom.xml b/test-framework/pom.xml index 075df12ddfb11..15092a6c30d42 100644 --- a/test-framework/pom.xml +++ b/test-framework/pom.xml @@ -43,6 +43,7 @@ mongodb kafka-companion google-cloud-functions + observability From 82955d1d59a0a584a16605b04f895caa97c0b8e4 Mon Sep 17 00:00:00 2001 From: brunobat Date: Sun, 2 Feb 2025 19:42:29 +0000 Subject: [PATCH 2/2] Reduce load and system stress by polling less with awatility --- .../src/main/resources/application.properties | 3 +++ .../java/io/quarkus/observability/test/LgtmTest.java | 3 +++ .../io/quarkus/observability/test/LgtmTestHelper.java | 4 +++- .../quarkus/observability/test/utils/GrafanaClient.java | 3 +++ .../java/io/quarkus/observability/test/utils/User.java | 9 +++++++++ 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties b/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties index f7dcd93e1974e..c30ddf92a229c 100644 --- a/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties +++ b/integration-tests/observability-lgtm-prometheus/src/main/resources/application.properties @@ -2,3 +2,6 @@ quarkus.log.category."io.quarkus.observability".level=DEBUG quarkus.log.category."io.quarkus.devservices".level=DEBUG quarkus.micrometer.binder-enabled-default=false + +quarkus.http.access-log.enabled=true +quarkus.http.access-log.pattern=%h %l %u %t "%r" %s %b %m "%{i,Referer}" "%{i,User-Agent}" "%{i,X-Request-Id}" "%{i,X-Organization-Id}" %D diff --git a/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java b/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java index e57206abf14de..05cfbc9bc3b5e 100644 --- a/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java +++ b/integration-tests/observability-lgtm-prometheus/src/test/java/io/quarkus/observability/test/LgtmTest.java @@ -32,6 +32,9 @@ public void testPoke() { String response = RestAssured.get("/api/poke?f=100").body().asString(); log.info("Response: " + response); GrafanaClient client = new GrafanaClient(endpoint, "admin", "admin"); + + Awaitility.setDefaultPollInterval(1, TimeUnit.SECONDS); // reduce load on the server. Default is .1s + Awaitility.await().atMost(61, TimeUnit.SECONDS).until( client::user, u -> "admin".equals(u.login)); diff --git a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java index e08f1adbafbce..a8c2c5742665c 100644 --- a/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java +++ b/integration-tests/observability-lgtm/src/test/java/io/quarkus/observability/test/LgtmTestHelper.java @@ -18,6 +18,9 @@ protected void poke(String path) { String response = RestAssured.get(path + "/poke?f=100").body().asString(); log.info("Response: " + response); GrafanaClient client = new GrafanaClient(grafanaEndpoint(), "admin", "admin"); + + Awaitility.setDefaultPollInterval(1, TimeUnit.SECONDS); // reduce load on the server. Default is .1s + Awaitility.await().atMost(61, TimeUnit.SECONDS).until( client::user, u -> "admin".equals(u.login)); @@ -28,5 +31,4 @@ protected void poke(String path) { () -> client.traces("quarkus-integration-test-observability-lgtm", 20, 3), result -> !result.traces.isEmpty()); } - } diff --git a/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java index 411b6aa92a89b..b7b4e433c43e9 100644 --- a/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/GrafanaClient.java @@ -87,6 +87,7 @@ public User user() { throw new UncheckedIOException(e); } }); + System.out.println("User: " + ref.get()); return ref.get(); } @@ -104,6 +105,7 @@ public QueryResult query(String query) { throw new UncheckedIOException(e); } }); + System.out.println("Query: " + ref.get()); return ref.get(); } @@ -123,6 +125,7 @@ public TempoResult traces(String service, int limit, int spss) { throw new UncheckedIOException(e); } }); + System.out.println("Traces: " + ref.get()); return ref.get(); } } diff --git a/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java index f07c68dac8e81..42552f8205f05 100644 --- a/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java +++ b/test-framework/observability/src/main/java/io/quarkus/observability/test/utils/User.java @@ -11,4 +11,13 @@ public class User { public String email; @JsonProperty public String login; + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", email='" + email + '\'' + + ", login='" + login + '\'' + + '}'; + } }