Skip to content

Commit f886e7b

Browse files
authored
Merge pull request #45747 from alesj/dash_1
Improvements to the Grafana LGTM dashboards - part 2
2 parents ed112c4 + dd2b104 commit f886e7b

File tree

24 files changed

+530
-36
lines changed

24 files changed

+530
-36
lines changed

extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/ContainerConstants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,12 @@ public final class ContainerConstants {
1515

1616
public static final String OTEL_GRPC_PROTOCOL = "grpc";
1717
public static final String OTEL_HTTP_PROTOCOL = "http/protobuf";
18+
19+
// Overrides
20+
21+
public static final int SCRAPING_INTERVAL = 10;
22+
public static final String OTEL_METRIC_EXPORT_INTERVAL = "10s";
23+
public static final String OTEL_BSP_SCHEDULE_DELAY = "3s";
24+
public static final String OTEL_BLRP_SCHEDULE_DELAY = "1s";
25+
1826
}

extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/ContainerConfigUtil.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
import java.lang.reflect.Method;
44
import java.util.Arrays;
5+
import java.util.HashMap;
6+
import java.util.Map;
57
import java.util.Objects;
8+
import java.util.Optional;
69

710
public class ContainerConfigUtil {
811
/**
@@ -16,11 +19,7 @@ public static boolean isEqual(ContainerConfig cc1, ContainerConfig cc2) {
1619
return false;
1720
}
1821

19-
Class<?> i = Arrays.stream(c1.getInterfaces())
20-
.filter(ContainerConfig.class::isAssignableFrom)
21-
.findFirst()
22-
.orElseThrow(() -> new IllegalArgumentException("Missing ContainerConfig based interface"));
23-
Method[] methods = i.getMethods(); // should get all config methods
22+
Method[] methods = getMethods(c1);
2423
for (Method m : methods) {
2524
Object v1 = invoke(m, cc1);
2625
Object v2 = invoke(m, cc2);
@@ -31,6 +30,38 @@ public static boolean isEqual(ContainerConfig cc1, ContainerConfig cc2) {
3130
return true;
3231
}
3332

33+
/**
34+
* Get all properties to override from container config instance.
35+
*
36+
* @param config the container config
37+
* @return map of properties to override
38+
*/
39+
public static Map<String, Object> propertiesToOverride(ContainerConfig config) {
40+
Map<String, Object> map = new HashMap<>();
41+
for (Method m : getMethods(config.getClass())) {
42+
OverrideProperty override = m.getAnnotation(OverrideProperty.class);
43+
if (override != null) {
44+
String key = override.value();
45+
Object value = invoke(m, config);
46+
if (value instanceof Optional<?>) {
47+
Optional<?> optional = (Optional<?>) value;
48+
optional.ifPresent(o -> map.put(key, o));
49+
} else if (value != null) {
50+
map.put(key, value);
51+
}
52+
}
53+
}
54+
return map;
55+
}
56+
57+
private static Method[] getMethods(Class<?> c1) {
58+
Class<?> i = Arrays.stream(c1.getInterfaces())
59+
.filter(ContainerConfig.class::isAssignableFrom)
60+
.findFirst()
61+
.orElseThrow(() -> new IllegalArgumentException("Missing ContainerConfig based interface"));
62+
return i.getMethods();
63+
}
64+
3465
private static Object invoke(Method m, Object target) {
3566
try {
3667
return m.invoke(target);

extensions/observability-devservices/common/src/main/java/io/quarkus/observability/common/config/LgtmConfig.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Set;
55

66
import io.quarkus.observability.common.ContainerConstants;
7+
import io.quarkus.runtime.annotations.ConfigDocIgnore;
78
import io.quarkus.runtime.annotations.ConfigGroup;
89
import io.smallrye.config.WithDefault;
910

@@ -37,4 +38,39 @@ public interface LgtmConfig extends GrafanaConfig {
3738
*/
3839
@WithDefault(ContainerConstants.OTEL_HTTP_PROTOCOL)
3940
String otlpProtocol();
41+
42+
/**
43+
* The (Prometheus) scraping interval, in seconds.
44+
*/
45+
@WithDefault(ContainerConstants.SCRAPING_INTERVAL + "")
46+
int scrapingInterval();
47+
48+
/**
49+
* Do we force scraping.
50+
*/
51+
Optional<Boolean> forceScraping();
52+
53+
/**
54+
* A way to override `quarkus.otel.metric.export.interval` property's default value.
55+
*/
56+
@OverrideProperty("quarkus.otel.metric.export.interval")
57+
@WithDefault(ContainerConstants.OTEL_METRIC_EXPORT_INTERVAL)
58+
@ConfigDocIgnore
59+
String otelMetricExportInterval();
60+
61+
/**
62+
* A way to override `quarkus.otel.bsp.schedule.delay` property's default value.
63+
*/
64+
@OverrideProperty("quarkus.otel.bsp.schedule.delay")
65+
@WithDefault(ContainerConstants.OTEL_BSP_SCHEDULE_DELAY)
66+
@ConfigDocIgnore
67+
String otelBspScheduleDelay();
68+
69+
/**
70+
* A way to override `quarkus.otel.metric.export.interval` property's default value.
71+
*/
72+
@OverrideProperty("quarkus.otel.blrp.schedule.delay")
73+
@WithDefault(ContainerConstants.OTEL_BLRP_SCHEDULE_DELAY)
74+
@ConfigDocIgnore
75+
String otelBlrpScheduleDelay();
4076
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.quarkus.observability.common.config;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* Override the property in the value,
10+
* with the value of the annotated method's return.
11+
*/
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target({ ElementType.METHOD })
14+
public @interface OverrideProperty {
15+
/**
16+
* The property key to override.
17+
*
18+
* @return the property key
19+
*/
20+
String value();
21+
}

extensions/observability-devservices/deployment/src/main/java/io/quarkus/observability/deployment/ObservabilityDevServiceProcessor.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.quarkus.deployment.builditem.DockerStatusBuildItem;
2929
import io.quarkus.deployment.builditem.FeatureBuildItem;
3030
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
31+
import io.quarkus.deployment.builditem.RunTimeConfigurationDefaultBuildItem;
3132
import io.quarkus.deployment.console.ConsoleInstalledBuildItem;
3233
import io.quarkus.deployment.console.StartupLogCompressor;
3334
import io.quarkus.deployment.dev.devservices.DevServicesConfig;
@@ -84,6 +85,7 @@ public void startContainers(LaunchModeBuildItem launchMode,
8485
LoggingSetupBuildItem loggingSetupBuildItem,
8586
DevServicesConfig devServicesConfig,
8687
BuildProducer<DevServicesResultBuildItem> services,
88+
BuildProducer<RunTimeConfigurationDefaultBuildItem> properties,
8789
Capabilities capabilities,
8890
Optional<MetricsCapabilityBuildItem> metricsConfiguration,
8991
BuildProducer<ObservabilityDevServicesConfigBuildItem> configBuildProducer) {
@@ -118,6 +120,8 @@ public void startContainers(LaunchModeBuildItem launchMode,
118120
ContainerConfig currentDevServicesConfiguration = dev.config(
119121
configuration,
120122
new ExtensionsCatalog(
123+
QuarkusClassLoader::isResourcePresentAtRuntime,
124+
QuarkusClassLoader::isClassPresentAtRuntime,
121125
capabilities.isPresent(Capability.OPENTELEMETRY_TRACER),
122126
hasMicrometerOtlp(metricsConfiguration)));
123127

@@ -140,6 +144,13 @@ public void startContainers(LaunchModeBuildItem launchMode,
140144
devServices.remove(devId); // clean-up
141145
capturedDevServicesConfigurations.put(devId, currentDevServicesConfiguration);
142146

147+
// override some OTel, etc defaults - rates, intervals, delays, ...
148+
Map<String, Object> propertiesToOverride = ContainerConfigUtil
149+
.propertiesToOverride(currentDevServicesConfiguration);
150+
propertiesToOverride
151+
.forEach((k, v) -> properties.produce(new RunTimeConfigurationDefaultBuildItem(k, v.toString())));
152+
log.infof("Dev Service %s properties override: %s", devId, propertiesToOverride);
153+
143154
StartupLogCompressor compressor = new StartupLogCompressor(
144155
(launchMode.isTest() ? "(test) " : "") + devId + " Dev Services Starting:",
145156
consoleInstalledBuildItem,

extensions/observability-devservices/testcontainers/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
<name>Quarkus - Observability Dev Services - Testcontainers</name>
1414

1515
<dependencies>
16+
<dependency>
17+
<groupId>io.quarkus</groupId>
18+
<artifactId>quarkus-devtools-utilities</artifactId>
19+
</dependency>
1620
<dependency>
1721
<groupId>io.quarkus</groupId>
1822
<artifactId>quarkus-devservices-common</artifactId>

extensions/observability-devservices/testcontainers/src/main/java/io/quarkus/observability/testcontainers/LgtmContainer.java

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
import io.quarkus.observability.common.ContainerConstants;
1616
import io.quarkus.observability.common.config.AbstractGrafanaConfig;
1717
import io.quarkus.observability.common.config.LgtmConfig;
18+
import io.quarkus.runtime.LaunchMode;
19+
import io.quarkus.utilities.OS;
1820

21+
@SuppressWarnings("resource")
1922
public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
2023
protected static final String LGTM_NETWORK_ALIAS = "ltgm.testcontainer.docker";
2124

22-
protected static final String PROMETHEUS_CONFIG = """
25+
protected static final String PROMETHEUS_CONFIG_DEFAULT = """
2326
---
2427
otlp:
2528
# Recommended attributes to be promoted to labels.
@@ -47,12 +50,15 @@ public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
4750
# A 10min time window is enough because it can easily absorb retries and network delays.
4851
out_of_order_time_window: 10m
4952
global:
50-
scrape_interval: 5s
53+
scrape_interval: %s
5154
evaluation_interval: 5s
55+
""";
56+
57+
protected static final String PROMETHEUS_CONFIG_SCRAPE = """
5258
scrape_configs:
5359
- job_name: '%s'
5460
metrics_path: '%s%s'
55-
scrape_interval: 5s
61+
scrape_interval: %s
5662
static_configs:
5763
- targets: ['%s:%d']
5864
""";
@@ -83,12 +89,16 @@ public class LgtmContainer extends GrafanaContainer<LgtmContainer, LgtmConfig> {
8389
foldersFromFilesStructure: false
8490
""";
8591

86-
public LgtmContainer() {
87-
this(new LgtmConfigImpl());
92+
private final boolean scrapingRequired;
93+
94+
public LgtmContainer(boolean scrapingRequired) {
95+
this(new LgtmConfigImpl(), scrapingRequired);
8896
}
8997

90-
public LgtmContainer(LgtmConfig config) {
98+
public LgtmContainer(LgtmConfig config, boolean scrapingRequired) {
9199
super(config);
100+
// do we require scraping
101+
this.scrapingRequired = scrapingRequired;
92102
// always expose both -- since the LGTM image already does that as well
93103
addExposedPorts(ContainerConstants.OTEL_GRPC_EXPORTER_PORT, ContainerConstants.OTEL_HTTP_EXPORTER_PORT);
94104

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

111121
addFileToContainer(getPrometheusConfig().getBytes(), "/otel-lgtm/prometheus.yaml");
112-
113122
}
114123

115124
@Override
@@ -136,11 +145,6 @@ public String getOtlpProtocol() {
136145
return config.otlpProtocol();
137146
}
138147

139-
public int getOtlpPort() {
140-
int port = getPrivateOtlpPort();
141-
return getMappedPort(port);
142-
}
143-
144148
private int getPrivateOtlpPort() {
145149
return getPrivateOtlpPort(getOtlpProtocol());
146150
}
@@ -157,12 +161,31 @@ public static int getPrivateOtlpPort(String otlpProtocol) {
157161
}
158162

159163
private String getPrometheusConfig() {
160-
Config runtimeConfig = ConfigProvider.getConfig();
161-
String rootPath = runtimeConfig.getOptionalValue("quarkus.management.root-path", String.class).orElse("/q");
162-
String metricsPath = runtimeConfig.getOptionalValue("quarkus.management.metrics.path", String.class).orElse("/metrics");
163-
int httpPort = runtimeConfig.getOptionalValue("quarkus.http.port", Integer.class).orElse(8080); // when not set use default
164-
165-
return String.format(PROMETHEUS_CONFIG, config.serviceName(), rootPath, metricsPath, "host.docker.internal", httpPort);
164+
String scraping = config.scrapingInterval() + "s";
165+
String prometheusConfig = String.format(PROMETHEUS_CONFIG_DEFAULT, scraping);
166+
if (config.forceScraping().orElse(scrapingRequired)) {
167+
boolean isTest = LaunchMode.current() == LaunchMode.TEST;
168+
Config runtimeConfig = ConfigProvider.getConfig();
169+
String rootPath = runtimeConfig.getOptionalValue("quarkus.management.root-path", String.class).orElse("/q");
170+
String metricsPath = runtimeConfig.getOptionalValue("quarkus.management.metrics.path", String.class)
171+
.orElse("/metrics");
172+
String httpPortKey = isTest ? "quarkus.http.test-port" : "quarkus.http.port";
173+
Optional<Integer> optionalValue = runtimeConfig.getOptionalValue(httpPortKey, Integer.class);
174+
int httpPort = optionalValue.orElse(isTest ? 8081 : 8080); // when not set use default
175+
176+
// On Linux, you can’t automatically resolve host.docker.internal,
177+
// you need to provide the following run flag when you start the container:
178+
//--add-host=host.docker.internal:host-gateway
179+
if (OS.determineOS() == OS.LINUX) {
180+
withCreateContainerCmdModifier(cmd -> cmd
181+
.getHostConfig()
182+
.withExtraHosts("host.docker.internal:host-gateway"));
183+
}
184+
185+
prometheusConfig += String.format(PROMETHEUS_CONFIG_SCRAPE, config.serviceName(), rootPath, metricsPath, scraping,
186+
"host.docker.internal", httpPort);
187+
}
188+
return prometheusConfig;
166189
}
167190

168191
protected static class LgtmConfigImpl extends AbstractGrafanaConfig implements LgtmConfig {
@@ -183,6 +206,31 @@ public Optional<Set<String>> networkAliases() {
183206
public String otlpProtocol() {
184207
return ContainerConstants.OTEL_HTTP_PROTOCOL;
185208
}
209+
210+
@Override
211+
public int scrapingInterval() {
212+
return ContainerConstants.SCRAPING_INTERVAL;
213+
}
214+
215+
@Override
216+
public Optional<Boolean> forceScraping() {
217+
return Optional.empty();
218+
}
219+
220+
@Override
221+
public String otelMetricExportInterval() {
222+
return ContainerConstants.OTEL_METRIC_EXPORT_INTERVAL;
223+
}
224+
225+
@Override
226+
public String otelBspScheduleDelay() {
227+
return ContainerConstants.OTEL_BSP_SCHEDULE_DELAY;
228+
}
229+
230+
@Override
231+
public String otelBlrpScheduleDelay() {
232+
return ContainerConstants.OTEL_BLRP_SCHEDULE_DELAY;
233+
}
186234
}
187235

188236
protected static class LgtmLoggingFilter implements Predicate<OutputFrame> {
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
package io.quarkus.observability.devresource;
22

3+
import java.util.function.Function;
4+
35
/**
46
* Relevant Observability extensions present.
57
*/
6-
public record ExtensionsCatalog(boolean hasOpenTelemetry,
8+
public record ExtensionsCatalog(
9+
Function<String, Boolean> resourceChecker,
10+
Function<String, Boolean> classChecker,
11+
boolean hasOpenTelemetry,
712
boolean hasMicrometerOtlp) {
813
}

0 commit comments

Comments
 (0)