Skip to content

Commit

Permalink
Merge pull request quarkusio#45747 from alesj/dash_1
Browse files Browse the repository at this point in the history
Improvements to the Grafana LGTM dashboards - part 2
  • Loading branch information
brunobat authored Feb 5, 2025
2 parents ed112c4 + dd2b104 commit f886e7b
Show file tree
Hide file tree
Showing 24 changed files with 530 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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";

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
/**
Expand All @@ -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);
Expand All @@ -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<String, Object> propertiesToOverride(ContainerConfig config) {
Map<String, Object> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<Boolean> 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();
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,6 +85,7 @@ public void startContainers(LaunchModeBuildItem launchMode,
LoggingSetupBuildItem loggingSetupBuildItem,
DevServicesConfig devServicesConfig,
BuildProducer<DevServicesResultBuildItem> services,
BuildProducer<RunTimeConfigurationDefaultBuildItem> properties,
Capabilities capabilities,
Optional<MetricsCapabilityBuildItem> metricsConfiguration,
BuildProducer<ObservabilityDevServicesConfigBuildItem> configBuildProducer) {
Expand Down Expand Up @@ -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)));

Expand All @@ -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<String, Object> 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,
Expand Down
4 changes: 4 additions & 0 deletions extensions/observability-devservices/testcontainers/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<name>Quarkus - Observability Dev Services - Testcontainers</name>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-utilities</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-common</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@
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;
import io.quarkus.utilities.OS;

@SuppressWarnings("resource")
public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
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.
Expand Down Expand Up @@ -47,12 +50,15 @@ public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
# 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']
""";
Expand Down Expand Up @@ -83,12 +89,16 @@ public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
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);

Expand All @@ -109,7 +119,6 @@ public LgtmContainer(LgtmConfig config) {
"/otel-lgtm/grafana-dashboard-opentelemetry-logging.json");

addFileToContainer(getPrometheusConfig().getBytes(), "/otel-lgtm/prometheus.yaml");

}

@Override
Expand All @@ -136,11 +145,6 @@ public String getOtlpProtocol() {
return config.otlpProtocol();
}

public int getOtlpPort() {
int port = getPrivateOtlpPort();
return getMappedPort(port);
}

private int getPrivateOtlpPort() {
return getPrivateOtlpPort(getOtlpProtocol());
}
Expand All @@ -157,12 +161,31 @@ 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<Integer> optionalValue = runtimeConfig.getOptionalValue(httpPortKey, Integer.class);
int httpPort = optionalValue.orElse(isTest ? 8081 : 8080); // when not set use default

// On Linux, you can’t automatically resolve host.docker.internal,
// you need to provide the following run flag when you start the container:
//--add-host=host.docker.internal:host-gateway
if (OS.determineOS() == OS.LINUX) {
withCreateContainerCmdModifier(cmd -> cmd
.getHostConfig()
.withExtraHosts("host.docker.internal:host-gateway"));
}

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 {
Expand All @@ -183,6 +206,31 @@ public Optional<Set<String>> networkAliases() {
public String otlpProtocol() {
return ContainerConstants.OTEL_HTTP_PROTOCOL;
}

@Override
public int scrapingInterval() {
return ContainerConstants.SCRAPING_INTERVAL;
}

@Override
public Optional<Boolean> 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<OutputFrame> {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, Boolean> resourceChecker,
Function<String, Boolean> classChecker,
boolean hasOpenTelemetry,
boolean hasMicrometerOtlp) {
}
Loading

0 comments on commit f886e7b

Please sign in to comment.