Skip to content

Commit fd8fe90

Browse files
authored
yaml jmx metrics test infrastructure (#13597)
1 parent d503937 commit fd8fe90

File tree

9 files changed

+908
-1
lines changed

9 files changed

+908
-1
lines changed

instrumentation/jmx-metrics/javaagent/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ rules:
302302
unit: By
303303
metricAttribute:
304304
jvm.memory.pool.name : param(name)
305-
jvm.memory.type: lowercase(beanattr(type))
305+
jvm.memory.type: lowercase(beanattr(Type))
306306
```
307307

308308
For now, only the `lowercase` transformation is supported, other additions might be added in the future if needed.

instrumentation/jmx-metrics/library/build.gradle.kts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2+
13
plugins {
24
id("otel.library-instrumentation")
35
}
@@ -6,4 +8,28 @@ dependencies {
68
implementation("org.snakeyaml:snakeyaml-engine")
79

810
testImplementation(project(":testing-common"))
11+
testImplementation("org.testcontainers:testcontainers")
12+
13+
testImplementation("org.testcontainers:junit-jupiter")
14+
testImplementation("com.linecorp.armeria:armeria-junit5:1.31.3")
15+
testImplementation("com.linecorp.armeria:armeria-junit5:1.31.3")
16+
testImplementation("com.linecorp.armeria:armeria-grpc:1.31.3")
17+
testImplementation("io.opentelemetry.proto:opentelemetry-proto:1.5.0-alpha")
18+
}
19+
20+
tasks {
21+
test {
22+
// get packaged agent jar for testing
23+
val shadowTask = project(":javaagent").tasks.named<ShadowJar>("shadowJar").get()
24+
25+
dependsOn(shadowTask)
26+
27+
inputs.files(layout.files(shadowTask))
28+
.withPropertyName("javaagent")
29+
.withNormalizer(ClasspathNormalizer::class)
30+
31+
doFirst {
32+
jvmArgs("-Dio.opentelemetry.javaagent.path=${shadowTask.archiveFile.get()}")
33+
}
34+
}
935
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.jmx.rules;
7+
8+
import static io.opentelemetry.instrumentation.jmx.rules.assertions.JmxAssertj.assertThat;
9+
import static org.assertj.core.api.Assertions.fail;
10+
11+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
12+
import io.opentelemetry.instrumentation.jmx.rules.assertions.MetricAssert;
13+
import io.opentelemetry.proto.metrics.v1.Metric;
14+
import java.util.HashMap;
15+
import java.util.HashSet;
16+
import java.util.List;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.function.Consumer;
20+
import java.util.stream.Collectors;
21+
22+
public class MetricsVerifier {
23+
24+
private final Map<String, Consumer<Metric>> assertions = new HashMap<>();
25+
private boolean strictMode = true;
26+
27+
private MetricsVerifier() {}
28+
29+
/**
30+
* Create instance of MetricsVerifier configured to fail verification if any metric was not
31+
* verified because there is no assertion defined for it. This behavior can be changed by calling
32+
* {@link #disableStrictMode()} method.
33+
*
34+
* @return new instance of MetricsVerifier
35+
* @see #disableStrictMode()
36+
*/
37+
public static MetricsVerifier create() {
38+
return new MetricsVerifier();
39+
}
40+
41+
/**
42+
* Disable strict checks of metric assertions. It means that all metrics checks added after
43+
* calling this method will not enforce asserting all metric properties and will not detect
44+
* duplicate property assertions. Also, there will be no error reported if any of metrics was
45+
* skipped because no assertion was added for it.
46+
*
47+
* @return this
48+
* @see #verify(List)
49+
* @see #add(String, Consumer)
50+
*/
51+
@CanIgnoreReturnValue
52+
public MetricsVerifier disableStrictMode() {
53+
strictMode = false;
54+
return this;
55+
}
56+
57+
/**
58+
* Add assertion for given metric
59+
*
60+
* @param metricName name of metric to be verified by provided assertion
61+
* @param assertion an assertion to verify properties of the metric
62+
* @return this
63+
*/
64+
@CanIgnoreReturnValue
65+
public MetricsVerifier add(String metricName, Consumer<MetricAssert> assertion) {
66+
if (assertions.containsKey(metricName)) {
67+
throw new IllegalArgumentException("Duplicate assertion for metric " + metricName);
68+
}
69+
assertions.put(
70+
metricName,
71+
metric -> {
72+
MetricAssert metricAssert = assertThat(metric);
73+
metricAssert.setStrict(strictMode);
74+
assertion.accept(metricAssert);
75+
metricAssert.strictCheck();
76+
});
77+
return this;
78+
}
79+
80+
/**
81+
* Execute all defined assertions against provided list of metrics. Error is reported if any of
82+
* defined assertions failed. Error is also reported if any of expected metrics was not present in
83+
* the metrics list, unless strict mode is disabled with {@link #disableStrictMode()}.
84+
*
85+
* @param metrics list of metrics to be verified
86+
* @see #add(String, Consumer)
87+
* @see #disableStrictMode()
88+
*/
89+
public void verify(List<Metric> metrics) {
90+
verifyAllExpectedMetricsWereReceived(metrics);
91+
92+
Set<String> unverifiedMetrics = new HashSet<>();
93+
94+
for (Metric metric : metrics) {
95+
String metricName = metric.getName();
96+
Consumer<Metric> assertion = assertions.get(metricName);
97+
98+
if (assertion != null) {
99+
assertion.accept(metric);
100+
} else {
101+
unverifiedMetrics.add(metricName);
102+
}
103+
}
104+
105+
if (strictMode && !unverifiedMetrics.isEmpty()) {
106+
fail("Metrics received but not verified because no assertion exists: " + unverifiedMetrics);
107+
}
108+
}
109+
110+
private void verifyAllExpectedMetricsWereReceived(List<Metric> metrics) {
111+
Set<String> receivedMetricNames =
112+
metrics.stream().map(Metric::getName).collect(Collectors.toSet());
113+
Set<String> assertionNames = new HashSet<>(assertions.keySet());
114+
115+
assertionNames.removeAll(receivedMetricNames);
116+
if (!assertionNames.isEmpty()) {
117+
fail(
118+
"Metrics expected but not received: "
119+
+ assertionNames
120+
+ "\nReceived only: "
121+
+ receivedMetricNames);
122+
}
123+
}
124+
}

0 commit comments

Comments
 (0)