diff --git a/modules/grafana/build.gradle b/modules/grafana/build.gradle
index 34a45cc404b..dbbcb7e1ded 100644
--- a/modules/grafana/build.gradle
+++ b/modules/grafana/build.gradle
@@ -7,6 +7,11 @@ dependencies {
testImplementation 'io.rest-assured:rest-assured:5.5.0'
testImplementation 'io.micrometer:micrometer-registry-otlp:1.13.4'
testImplementation 'uk.org.webcompere:system-stubs-junit4:2.1.6'
+
+ testImplementation platform('io.opentelemetry:opentelemetry-bom:1.49.0')
+ testImplementation 'io.opentelemetry:opentelemetry-api'
+ testImplementation 'io.opentelemetry:opentelemetry-sdk'
+ testImplementation 'io.opentelemetry:opentelemetry-exporter-otlp'
}
tasks.japicmp {
diff --git a/modules/grafana/src/main/java/org/testcontainers/grafana/LgtmStackContainer.java b/modules/grafana/src/main/java/org/testcontainers/grafana/LgtmStackContainer.java
index 595d016772a..00299ac27f2 100644
--- a/modules/grafana/src/main/java/org/testcontainers/grafana/LgtmStackContainer.java
+++ b/modules/grafana/src/main/java/org/testcontainers/grafana/LgtmStackContainer.java
@@ -14,6 +14,7 @@
* Exposed ports:
*
* - Grafana: 3000
+ * - Tempo: 3200
* - OTel Http: 4317
* - OTel Grpc: 4318
* - Prometheus: 9090
@@ -30,6 +31,8 @@ public class LgtmStackContainer extends GenericContainer {
private static final int OTLP_HTTP_PORT = 4318;
+ private static final int TEMPO_PORT = 3200;
+
private static final int PROMETHEUS_PORT = 9090;
public LgtmStackContainer(String image) {
@@ -39,7 +42,7 @@ public LgtmStackContainer(String image) {
public LgtmStackContainer(DockerImageName image) {
super(image);
image.assertCompatibleWith(DEFAULT_IMAGE_NAME);
- withExposedPorts(GRAFANA_PORT, OTLP_GRPC_PORT, OTLP_HTTP_PORT, PROMETHEUS_PORT);
+ withExposedPorts(GRAFANA_PORT, TEMPO_PORT, OTLP_GRPC_PORT, OTLP_HTTP_PORT, PROMETHEUS_PORT);
waitingFor(
Wait.forLogMessage(".*The OpenTelemetry collector and the Grafana LGTM stack are up and running.*\\s", 1)
);
@@ -54,6 +57,10 @@ public String getOtlpGrpcUrl() {
return "http://" + getHost() + ":" + getMappedPort(OTLP_GRPC_PORT);
}
+ public String getTempoUrl() {
+ return "http://" + getHost() + ":" + getMappedPort(TEMPO_PORT);
+ }
+
public String getOtlpHttpUrl() {
return "http://" + getHost() + ":" + getMappedPort(OTLP_HTTP_PORT);
}
diff --git a/modules/grafana/src/test/java/org/testcontainers/grafana/LgtmStackContainerTest.java b/modules/grafana/src/test/java/org/testcontainers/grafana/LgtmStackContainerTest.java
index f5efe628f3c..a2b4f5a221a 100644
--- a/modules/grafana/src/test/java/org/testcontainers/grafana/LgtmStackContainerTest.java
+++ b/modules/grafana/src/test/java/org/testcontainers/grafana/LgtmStackContainerTest.java
@@ -5,6 +5,15 @@
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.registry.otlp.OtlpConfig;
import io.micrometer.registry.otlp.OtlpMeterRegistry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.resources.Resource;
+import io.opentelemetry.sdk.trace.SdkTracerProvider;
+import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.awaitility.Awaitility;
@@ -12,15 +21,16 @@
import uk.org.webcompere.systemstubs.SystemStubs;
import java.time.Duration;
+import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
public class LgtmStackContainerTest {
@Test
- public void shouldPublishMetric() throws Exception {
+ public void shouldPublishMetricAndTrace() throws Exception {
try ( // container {
- LgtmStackContainer lgtm = new LgtmStackContainer("grafana/otel-lgtm:0.6.0")
+ LgtmStackContainer lgtm = new LgtmStackContainer("grafana/otel-lgtm:0.11.0")
// }
) {
lgtm.start();
@@ -29,7 +39,9 @@ public void shouldPublishMetric() throws Exception {
.get(String.format("http://%s:%s/api/health", lgtm.getHost(), lgtm.getMappedPort(3000)))
.jsonPath()
.get("version");
- assertThat(version).isEqualTo("11.0.0");
+ assertThat(version).isEqualTo("11.6.0");
+
+ generateTrace(lgtm);
OtlpConfig otlpConfig = createOtlpConfig(lgtm);
MeterRegistry meterRegistry = SystemStubs
@@ -52,9 +64,52 @@ public void shouldPublishMetric() throws Exception {
assertThat(response.getStatusCode()).isEqualTo(200);
assertThat(response.body().jsonPath().getList("data.result[0].value")).contains("2");
});
+
+ Awaitility
+ .given()
+ .pollInterval(Duration.ofSeconds(2))
+ .atMost(Duration.ofSeconds(5))
+ .ignoreExceptions()
+ .untilAsserted(() -> {
+ Response response = RestAssured
+ .given()
+ .get(String.format("%s/api/search", lgtm.getTempoUrl()))
+ .prettyPeek()
+ .thenReturn();
+ assertThat(response.getStatusCode()).isEqualTo(200);
+ assertThat(response.body().jsonPath().getString("traces[0].rootServiceName"))
+ .isEqualTo("test-service");
+ });
}
}
+ private void generateTrace(LgtmStackContainer lgtm) {
+ OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter
+ .builder()
+ .setTimeout(Duration.ofSeconds(1))
+ .setEndpoint(lgtm.getOtlpGrpcUrl())
+ .build();
+
+ BatchSpanProcessor spanProcessor = BatchSpanProcessor
+ .builder(exporter)
+ .setScheduleDelay(500, TimeUnit.MILLISECONDS)
+ .build();
+
+ SdkTracerProvider tracerProvider = SdkTracerProvider
+ .builder()
+ .addSpanProcessor(spanProcessor)
+ .setResource(Resource.create(Attributes.of(AttributeKey.stringKey("service.name"), "test-service")))
+ .build();
+
+ OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).build();
+
+ Tracer tracer = openTelemetry.getTracer("test");
+ Span span = tracer.spanBuilder("test").startSpan();
+ span.end();
+
+ openTelemetry.shutdown();
+ }
+
private static OtlpConfig createOtlpConfig(LgtmStackContainer lgtm) {
return new OtlpConfig() {
@Override