Skip to content

Commit a0d95eb

Browse files
Reno Seobjrara
authored andcommitted
feat: Add contract tests for runtime metrics (aws-observability#893)
*Issue #, if available:* N/A *Description of changes:* This PR adds contract tests for runtime metrics feature, where it validates: 1. Are all runtime metrics captured? 2. Do the runtime metrics have a realistic value? (non-negative) These changes can easily be extended for higher coverage in the future. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 47ce3b1 commit a0d95eb

File tree

4 files changed

+124
-2
lines changed

4 files changed

+124
-2
lines changed

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/base/ContractTestBase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public abstract class ContractTestBase {
7979
.withEnv("JAVA_TOOL_OPTIONS", "-javaagent:/opentelemetry-javaagent-all.jar")
8080
.withEnv("OTEL_METRIC_EXPORT_INTERVAL", "100") // 100 ms
8181
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_ENABLED", "true")
82+
.withEnv("OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED", isRuntimeEnabled())
8283
.withEnv("OTEL_METRICS_EXPORTER", "none")
8384
.withEnv("OTEL_BSP_SCHEDULE_DELAY", "0") // Don't wait to export spans to the collector
8485
.withEnv(
@@ -159,4 +160,8 @@ protected String getApplicationOtelServiceName() {
159160
protected String getApplicationOtelResourceAttributes() {
160161
return "service.name=" + getApplicationOtelServiceName();
161162
}
163+
164+
protected String isRuntimeEnabled() {
165+
return "false";
166+
}
162167
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.opentelemetry.appsignals.test.misc;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import io.opentelemetry.proto.metrics.v1.NumberDataPoint;
21+
import java.util.List;
22+
import java.util.Set;
23+
import org.junit.Assert;
24+
import org.junit.jupiter.api.Nested;
25+
import org.junit.jupiter.api.Test;
26+
import org.junit.jupiter.api.TestInstance;
27+
import org.testcontainers.junit.jupiter.Testcontainers;
28+
import software.amazon.opentelemetry.appsignals.test.base.ContractTestBase;
29+
import software.amazon.opentelemetry.appsignals.test.utils.AppSignalsConstants;
30+
31+
public class RuntimeMetricsTest {
32+
private abstract static class RuntimeMetricsContractTestBase extends ContractTestBase {
33+
@Override
34+
protected String getApplicationImageName() {
35+
return "aws-appsignals-tests-http-server-spring-mvc";
36+
}
37+
38+
@Override
39+
protected String isRuntimeEnabled() {
40+
return "true";
41+
}
42+
43+
protected String getApplicationWaitPattern() {
44+
return ".*Started Application.*";
45+
}
46+
47+
protected void doTestRuntimeMetrics() {
48+
var response = appClient.get("/success").aggregate().join();
49+
50+
assertThat(response.status().isSuccess()).isTrue();
51+
assertRuntimeMetrics();
52+
}
53+
54+
protected void assertRuntimeMetrics() {
55+
var metrics =
56+
mockCollectorClient.getRuntimeMetrics(
57+
Set.of(
58+
AppSignalsConstants.JVM_GC_METRIC,
59+
AppSignalsConstants.JVM_GC_COUNT,
60+
AppSignalsConstants.JVM_HEAP_USED,
61+
AppSignalsConstants.JVM_NON_HEAP_USED,
62+
AppSignalsConstants.JVM_AFTER_GC,
63+
AppSignalsConstants.JVM_POOL_USED,
64+
AppSignalsConstants.JVM_THREAD_COUNT,
65+
AppSignalsConstants.JVM_CLASS_LOADED,
66+
AppSignalsConstants.JVM_CPU_TIME,
67+
AppSignalsConstants.JVM_CPU_UTILIZATION,
68+
AppSignalsConstants.LATENCY_METRIC,
69+
AppSignalsConstants.ERROR_METRIC,
70+
AppSignalsConstants.FAULT_METRIC));
71+
metrics.forEach(
72+
metric -> {
73+
var dataPoints = metric.getMetric().getGauge().getDataPointsList();
74+
assertNonNegativeValue(dataPoints);
75+
});
76+
}
77+
78+
private void assertNonNegativeValue(List<NumberDataPoint> dps) {
79+
dps.forEach(
80+
datapoint -> {
81+
Assert.assertTrue(datapoint.getAsInt() >= 0);
82+
});
83+
}
84+
}
85+
86+
@Testcontainers(disabledWithoutDocker = true)
87+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
88+
@Nested
89+
class ValidateRuntimeMetricsTest extends RuntimeMetricsContractTestBase {
90+
@Test
91+
void testRuntimeMetrics() {
92+
doTestRuntimeMetrics();
93+
}
94+
}
95+
}

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/AppSignalsConstants.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,16 @@ public class AppSignalsConstants {
3333
public static final String AWS_REMOTE_RESOURCE_IDENTIFIER = "aws.remote.resource.identifier";
3434
public static final String AWS_SPAN_KIND = "aws.span.kind";
3535
public static final String AWS_REMOTE_DB_USER = "aws.remote.db.user";
36+
37+
// JVM Metrics
38+
public static final String JVM_GC_METRIC = "jvm.gc.collections.elapsed";
39+
public static final String JVM_GC_COUNT = "jvm.gc.collections.count";
40+
public static final String JVM_HEAP_USED = "jvm.memory.heap.used";
41+
public static final String JVM_NON_HEAP_USED = "jvm.memory.nonheap.used";
42+
public static final String JVM_AFTER_GC = "jvm.memory.pool.used_after_last_gc";
43+
public static final String JVM_POOL_USED = "jvm.memory.pool.used";
44+
public static final String JVM_THREAD_COUNT = "jvm.threads.count";
45+
public static final String JVM_CLASS_LOADED = "jvm.classes.loaded";
46+
public static final String JVM_CPU_TIME = "jvm.cpu.time";
47+
public static final String JVM_CPU_UTILIZATION = "jvm.cpu.recent_utilization";
3648
}

appsignals-tests/contract-tests/src/test/java/software/amazon/opentelemetry/appsignals/test/utils/MockCollectorClient.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,21 @@ public List<ResourceScopeSpan> getTraces() {
133133
.collect(toImmutableList());
134134
}
135135

136+
public List<ResourceScopeMetric> getRuntimeMetrics(Set<String> presentMetrics) {
137+
return fetchMetrics(presentMetrics, true);
138+
}
139+
140+
public List<ResourceScopeMetric> getMetrics(Set<String> presentMetrics) {
141+
return fetchMetrics(presentMetrics, false);
142+
}
143+
136144
/**
137145
* Get all metrics that are currently stored in the mock collector.
138146
*
139147
* @return List of `ResourceScopeMetric` which is a flat list containing all metrics and their
140148
* related scope and resources.
141149
*/
142-
public List<ResourceScopeMetric> getMetrics(Set<String> presentMetrics) {
150+
private List<ResourceScopeMetric> fetchMetrics(Set<String> presentMetrics, boolean isRuntime) {
143151
List<ExportMetricsServiceRequest> exportedMetrics =
144152
waitForContent(
145153
"/get-metrics",
@@ -153,7 +161,9 @@ public List<ResourceScopeMetric> getMetrics(Set<String> presentMetrics) {
153161
.map(x -> x.getName())
154162
.collect(Collectors.toSet());
155163

156-
return (!exported.isEmpty() && current.size() == exported.size())
164+
return (isRuntime
165+
? !exported.isEmpty() && receivedMetrics.size() == presentMetrics.size()
166+
: !exported.isEmpty() && current.size() == exported.size())
157167
&& receivedMetrics.containsAll(presentMetrics);
158168
});
159169

0 commit comments

Comments
 (0)