Verify in smoke tests that runtime metrics are exported
ADD
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/MetricsInspector.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.smoketest
+
+import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest
+
+class MetricsInspector {
+ final Collection<ExportMetricsServiceRequest> requests
+
+ MetricsInspector(Collection<ExportMetricsServiceRequest> requests) {
+ this.requests = requests
+ }
+
+ boolean hasMetricsNamed(String metricName) {
+ requests.stream()
+ .flatMap({ it.resourceMetricsList.stream() })
+ .flatMap({ it.instrumentationLibraryMetricsList.stream() })
+ .flatMap({ it.metricsList.stream() })
+ .anyMatch({ it.name == metricName })
+ }
+}

MODIFY
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SmokeTest.groovy
@@ -7,17 +7,14 @@ package io.opentelemetry.smoketest
import static java.util.stream.Collectors.toSet
-import com.fasterxml.jackson.databind.ObjectMapper
-import com.google.protobuf.util.JsonFormat
import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
+import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest
import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest
import io.opentelemetry.proto.common.v1.AnyValue
import io.opentelemetry.proto.trace.v1.Span
-import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import java.util.stream.Stream
import okhttp3.OkHttpClient
-import okhttp3.Request
import org.slf4j.LoggerFactory
import org.testcontainers.containers.GenericContainer
import org.testcontainers.containers.Network
@@ -33,13 +30,14 @@ import spock.lang.Specification
abstract class SmokeTest extends Specification {
private static final Pattern TRACE_ID_PATTERN = Pattern.compile(".*trace_id=(?<traceId>[a-zA-Z0-9]+).*")
- private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
-
protected static final OkHttpClient CLIENT = OkHttpUtils.client()
@Shared
private Backend backend = Backend.getInstance()
+ @Shared
+ private TelemetryRetriever telemetryRetriever
+
@Shared
protected String agentPath = System.getProperty("io.opentelemetry.smoketest.agent.shadowJar.path")
@@ -63,6 +61,7 @@ abstract class SmokeTest extends Specification {
def setupSpec() {
backend.setup()
+ telemetryRetriever = new TelemetryRetriever(backend.getMappedPort(8080))
}
def startTarget(int jdk, String serverVersion = null) {
@@ -79,7 +78,8 @@ abstract class SmokeTest extends Specification {
.withCopyFileToContainer(MountableFile.forHostPath(agentPath), "/opentelemetry-javaagent-all.jar")
.withEnv("JAVA_TOOL_OPTIONS", "-javaagent:/opentelemetry-javaagent-all.jar -Dio.opentelemetry.javaagent.slf4j.simpleLogger.log.muzzleMatcher=true")
.withEnv("OTEL_BSP_MAX_EXPORT_BATCH_SIZE", "1")
- .withEnv("OTEL_BSP_SCHEDULE_DELAY", "10")
+ .withEnv("OTEL_BSP_SCHEDULE_DELAY", "10ms")
+ .withEnv("OTEL_IMR_EXPORT_INTERVAL", "1000")
.withEnv("OTEL_EXPORTER_OTLP_ENDPOINT", "http://collector:55680")
.withImagePullPolicy(PullPolicy.alwaysPull())
.withEnv(extraEnv)
@@ -99,11 +99,7 @@ abstract class SmokeTest extends Specification {
}
def cleanup() {
- CLIENT.newCall(new Request.Builder()
- .url("http://localhost:${backend.getMappedPort(8080)}/clear-requests")
- .build())
- .execute()
- .close()
+ telemetryRetriever.clearTelemetry()
}
def stopTarget() {
@@ -135,40 +131,11 @@ abstract class SmokeTest extends Specification {
}
protected Collection<ExportTraceServiceRequest> waitForTraces() {
- def content = waitForContent()
-
- return OBJECT_MAPPER.readTree(content).collect {
- def builder = ExportTraceServiceRequest.newBuilder()
- // TODO(anuraaga): Register parser into object mapper to avoid de -> re -> deserialize.
- JsonFormat.parser().merge(OBJECT_MAPPER.writeValueAsString(it), builder)
- return builder.build()
- }
- }
-
- private String waitForContent() {
- long previousSize = 0
- long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30)
- String content = "[]"
- while (System.currentTimeMillis() < deadline) {
- def body = content = CLIENT.newCall(new Request.Builder()
- .url("http://localhost:${backend.getMappedPort(8080)}/get-requests")
- .build())
- .execute()
- .body()
- try {
- content = body.string()
- } finally {
- body.close()
- }
- if (content.length() > 2 && content.length() == previousSize) {
- break
- }
- previousSize = content.length()
- println "Curent content size $previousSize"
- TimeUnit.MILLISECONDS.sleep(500)
+ return telemetryRetriever.waitForTraces()
}
- return content
+ protected Collection<ExportMetricsServiceRequest> waitForMetrics() {
+ return telemetryRetriever.waitForMetrics()
}
protected static Set<String> getLoggedTraceIds(ToStringConsumer output) {
@@ -241,7 +208,7 @@ abstract class SmokeTest extends Specification {
started = true
Runtime.addShutdownHook { stop() }
- backend = new GenericContainer<>("ghcr.io/open-telemetry/java-test-containers:smoke-fake-backend-20201128.1734635")
+ backend = new GenericContainer<>("ghcr.io/open-telemetry/java-test-containers:smoke-fake-backend-20210319.060589")
.withExposedPorts(8080)
.waitingFor(Wait.forHttp("/health").forPort(8080))
.withNetwork(network)

MODIFY
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/SpringBootSmokeTest.groovy
@@ -32,15 +32,18 @@ class SpringBootSmokeTest extends SmokeTest {
def response = CLIENT.newCall(request).execute()
Collection<ExportTraceServiceRequest> traces = waitForTraces()
- then:
+ then: "spans are exported"
response.body().string() == "Hi!"
countSpansByName(traces, '/greeting') == 1
countSpansByName(traces, 'WebController.greeting') == 1
countSpansByName(traces, 'WebController.withSpan') == 1
+ then: "correct agent version is captured in the resource"
[currentAgentVersion] as Set == findResourceAttribute(traces, "telemetry.auto.version")
.map { it.stringValue }
.collect(toSet())
+
+ then: "OS is captured in the resource"
findResourceAttribute(traces, "os.type")
.map { it.stringValue }
.findAny()
@@ -56,6 +59,12 @@ class SpringBootSmokeTest extends SmokeTest {
.collect(toSet())
loggedTraceIds == spanTraceIds
+ then: "JVM metrics are exported"
+ def metrics = new MetricsInspector(waitForMetrics())
+ metrics.hasMetricsNamed("runtime.jvm.gc.collection")
+ metrics.hasMetricsNamed("runtime.jvm.memory.area")
+ metrics.hasMetricsNamed("runtime.jvm.memory.pool")
+
cleanup:
stopTarget()

ADD
smoke-tests/src/test/groovy/io/opentelemetry/smoketest/TelemetryRetriever.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.smoketest
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.google.protobuf.GeneratedMessageV3
+import com.google.protobuf.util.JsonFormat
+import io.opentelemetry.instrumentation.test.utils.OkHttpUtils
+import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest
+import io.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest
+import java.util.concurrent.TimeUnit
+import java.util.function.Supplier
+import okhttp3.OkHttpClient
+import okhttp3.Request
+
+class TelemetryRetriever {
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
+ protected static final OkHttpClient CLIENT = OkHttpUtils.client()
+
+ final int backendPort
+
+ TelemetryRetriever(int backendPort) {
+ this.backendPort = backendPort
+ }
+
+ void clearTelemetry() {
+ CLIENT.newCall(new Request.Builder()
+ .url("http://localhost:${backendPort}/clear")
+ .build())
+ .execute()
+ .close()
+ }
+
+ Collection<ExportTraceServiceRequest> waitForTraces() {
+ return waitForTelemetry("get-traces", { ExportTraceServiceRequest.newBuilder() })
+ }
+
+ Collection<ExportMetricsServiceRequest> waitForMetrics() {
+ return waitForTelemetry("get-metrics", { ExportMetricsServiceRequest.newBuilder() })
+ }
+
+ private <T extends GeneratedMessageV3, B extends GeneratedMessageV3.Builder> Collection<T> waitForTelemetry(String path, Supplier<B> builderConstructor) {
+ def content = waitForContent(path)
+
+ return OBJECT_MAPPER.readTree(content).collect {
+ def builder = builderConstructor.get()
+ // TODO(anuraaga): Register parser into object mapper to avoid de -> re -> deserialize.
+ JsonFormat.parser().merge(OBJECT_MAPPER.writeValueAsString(it), builder)
+ return (T) builder.build()
+ }
+ }
+
+ private String waitForContent(String path) {
+ long previousSize = 0
+ long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(30)
+ String content = "[]"
+ while (System.currentTimeMillis() < deadline) {
+ def body = content = CLIENT.newCall(new Request.Builder()
+ .url("http://localhost:${backendPort}/${path}")
+ .build())
+ .execute()
+ .body()
+ try {
+ content = body.string()
+ } finally {
+ body.close()
+ }
+ if (content.length() > 2 && content.length() == previousSize) {
+ break
+ }
+ previousSize = content.length()
+ println "Curent content size $previousSize"
+ TimeUnit.MILLISECONDS.sleep(500)
+ }
+
+ return content
+ }
+}

MODIFY
smoke-tests/src/test/resources/otel.yaml
smoke-tests/src/test/resources/otel.yaml
@@ -30,5 +30,9 @@ service:
receivers: [ otlp, zipkin, jaeger ]
processors: [ batch ]
exporters: [ logging, otlp ]
+ metrics:
+ receivers: [ otlp ]
+ processors: [ batch ]
+ exporters: [ logging, otlp ]
extensions: [ health_check, pprof, zpages ]

Verify in smoke tests that runtime metrics are exported (#2603)