Skip to content

Commit 2535a75

Browse files
jeanbisuttiMateusz Rzeszutektrask
authored
Add smoke test for the OTel Spring starter (open-telemetry#8965)
Co-authored-by: Mateusz Rzeszutek <[email protected]> Co-authored-by: Trask Stalnaker <[email protected]>
1 parent caa9b68 commit 2535a75

File tree

10 files changed

+319
-1
lines changed

10 files changed

+319
-1
lines changed

docs/contributing/running-tests.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,22 @@ If you are on Windows and you want to run the tests using linux containers:
5555
USE_LINUX_CONTAINERS=1 ./gradlew :smoke-tests:test -PsmokeTestSuite=payara
5656
```
5757

58+
# Smoke OpenTelemetry starter tests
59+
60+
Smoke tests for the [OpenTelemetry Spring starter](../../instrumentation/spring/starters/spring-boot-starter/README.md).
61+
62+
You can execute the tests in a JVM (`./gradlew smoke-tests-otel-starter:test`) or as Spring native tests (`./gradlew smoke-tests-otel-starter:nativeTest`).
63+
5864
## GraalVM native test
5965

60-
Some tests can be executed as GraalVM native executables:
66+
To execute all the instrumentation tests runnable as GraalVM native executables:
6167

6268
```
6369
./gradlew nativeTest
6470
```
6571

72+
[A Github workflow](../../.github/workflows/native-tests-daily.yml) executes the native tests every day.
73+
6674
## Docker disk space
6775

6876
Some of the instrumentation tests (and all of the smoke tests) spin up docker containers via

settings.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ hideFromDependabot(":smoke-tests:images:servlet:servlet-3.0")
132132
hideFromDependabot(":smoke-tests:images:servlet:servlet-5.0")
133133
hideFromDependabot(":smoke-tests:images:spring-boot")
134134

135+
include(":smoke-tests-otel-starter")
136+
135137
hideFromDependabot("instrumentation:akka:akka-actor-2.3:javaagent")
136138
hideFromDependabot(":instrumentation:akka:akka-actor-fork-join-2.5:javaagent")
137139
hideFromDependabot(":instrumentation:akka:akka-http-10.0:javaagent")
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
id("org.springframework.boot") version "3.1.0"
4+
id("org.graalvm.buildtools.native")
5+
}
6+
7+
description = "smoke-tests-otel-starter"
8+
9+
otelJava {
10+
minJavaVersionSupported.set(JavaVersion.VERSION_17)
11+
}
12+
13+
dependencies {
14+
implementation("org.springframework.boot:spring-boot-starter-web")
15+
implementation("org.springframework.boot:spring-boot-starter-data-jdbc")
16+
implementation("com.h2database:h2")
17+
implementation("org.apache.commons:commons-dbcp2")
18+
implementation(project(":instrumentation:jdbc:library"))
19+
implementation(project(":instrumentation:spring:starters:spring-boot-starter"))
20+
implementation(platform(org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES))
21+
22+
testImplementation("org.springframework.boot:spring-boot-starter-test")
23+
testImplementation(project(":testing-common"))
24+
}
25+
26+
tasks {
27+
compileAotJava {
28+
with(options) {
29+
compilerArgs.add("-Xlint:-deprecation,-unchecked,none")
30+
// To disable warnings/failure coming from the Java compiler during the Spring AOT processing
31+
// -deprecation,-unchecked and none are required (none is not enough)
32+
}
33+
}
34+
compileAotTestJava {
35+
with(options) {
36+
compilerArgs.add("-Xlint:-deprecation,-unchecked,none")
37+
// To disable warnings/failure coming from the Java compiler during the Spring AOT processing
38+
// -deprecation,-unchecked and none are required (none is not enough)
39+
}
40+
}
41+
checkstyleAot {
42+
isEnabled = false
43+
}
44+
checkstyleAotTest {
45+
isEnabled = false
46+
}
47+
}
48+
49+
// To be able to execute the tests as GraalVM native executables
50+
configurations.configureEach {
51+
exclude("org.apache.groovy", "groovy")
52+
exclude("org.apache.groovy", "groovy-json")
53+
exclude("org.spockframework", "spock-core")
54+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.spring.smoketest;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.instrumentation.jdbc.datasource.OpenTelemetryDataSource;
10+
import javax.sql.DataSource;
11+
import org.apache.commons.dbcp2.BasicDataSource;
12+
import org.springframework.context.annotation.Bean;
13+
import org.springframework.context.annotation.Configuration;
14+
15+
@Configuration
16+
public class DatasourceConfig {
17+
18+
@Bean
19+
public DataSource dataSource(OpenTelemetry openTelemetry) {
20+
BasicDataSource dataSource = new BasicDataSource();
21+
dataSource.setDriverClassName("org.h2.Driver");
22+
dataSource.setUrl("jdbc:h2:mem:db");
23+
dataSource.setUsername("username");
24+
dataSource.setPassword("pwd");
25+
return new OpenTelemetryDataSource(dataSource, openTelemetry);
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.spring.smoketest;
7+
8+
import org.springframework.boot.SpringApplication;
9+
import org.springframework.boot.autoconfigure.SpringBootApplication;
10+
import org.springframework.context.annotation.ImportRuntimeHints;
11+
12+
@SpringBootApplication
13+
@ImportRuntimeHints(RuntimeHints.class)
14+
public class OtelSpringStarterSmokeTestApplication {
15+
16+
public OtelSpringStarterSmokeTestApplication() {}
17+
18+
public static void main(String[] args) {
19+
SpringApplication.run(OtelSpringStarterSmokeTestApplication.class);
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.spring.smoketest;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.metrics.LongHistogram;
10+
import io.opentelemetry.api.metrics.Meter;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
@RestController
15+
public class OtelSpringStarterSmokeTestController {
16+
17+
public static final String URL = "/ping";
18+
public static final String TEST_HISTOGRAM = "histogram-test-otel-spring-starter";
19+
private final LongHistogram histogram;
20+
21+
public OtelSpringStarterSmokeTestController(OpenTelemetry openTelemetry) {
22+
Meter meter = openTelemetry.getMeter(OtelSpringStarterSmokeTestApplication.class.getName());
23+
histogram = meter.histogramBuilder(TEST_HISTOGRAM).ofLongs().build();
24+
}
25+
26+
@GetMapping(URL)
27+
public String ping() {
28+
histogram.record(10);
29+
return "pong";
30+
}
31+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.spring.smoketest;
7+
8+
import org.springframework.aot.hint.RuntimeHintsRegistrar;
9+
10+
// Necessary for GraalVM native test
11+
public class RuntimeHints implements RuntimeHintsRegistrar {
12+
13+
@Override
14+
public void registerHints(
15+
org.springframework.aot.hint.RuntimeHints hints, ClassLoader classLoader) {
16+
hints.resources().registerResourceBundle("org.apache.commons.dbcp2.LocalStrings");
17+
}
18+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.spring.smoketest;
7+
8+
import org.springframework.boot.context.event.ApplicationReadyEvent;
9+
import org.springframework.context.event.EventListener;
10+
import org.springframework.jdbc.core.JdbcTemplate;
11+
import org.springframework.stereotype.Component;
12+
13+
@Component
14+
public class SqlExecutor {
15+
16+
private final JdbcTemplate jdbcTemplate;
17+
18+
public SqlExecutor(JdbcTemplate jdbcTemplate) {
19+
this.jdbcTemplate = jdbcTemplate;
20+
}
21+
22+
@EventListener(ApplicationReadyEvent.class)
23+
public void loadData() {
24+
jdbcTemplate.execute("create table test_table (id bigint not null, primary key (id))");
25+
}
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
4+
<appender name="OpenTelemetry"
5+
class="io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender">
6+
</appender>
7+
8+
<root level="INFO">
9+
<appender-ref ref="OpenTelemetry"/>
10+
</root>
11+
12+
</configuration>
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.smoketest;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import io.opentelemetry.api.trace.SpanKind;
11+
import io.opentelemetry.sdk.logs.data.LogRecordData;
12+
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
13+
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
14+
import io.opentelemetry.sdk.metrics.data.MetricData;
15+
import io.opentelemetry.sdk.metrics.export.MetricExporter;
16+
import io.opentelemetry.sdk.testing.assertj.TracesAssert;
17+
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
18+
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricExporter;
19+
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
20+
import io.opentelemetry.sdk.trace.data.SpanData;
21+
import io.opentelemetry.sdk.trace.export.SpanExporter;
22+
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
23+
import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestApplication;
24+
import io.opentelemetry.spring.smoketest.OtelSpringStarterSmokeTestController;
25+
import java.util.List;
26+
import org.junit.jupiter.api.Test;
27+
import org.springframework.beans.factory.annotation.Autowired;
28+
import org.springframework.boot.test.context.SpringBootTest;
29+
import org.springframework.boot.test.web.client.TestRestTemplate;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
33+
@SpringBootTest(
34+
classes = {
35+
OtelSpringStarterSmokeTestApplication.class,
36+
OtelSpringStarterSmokeTest.TestConfiguration.class
37+
},
38+
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
39+
properties = {"otel.exporter.otlp.enabled=false", "otel.metric.export.interval=100"
40+
// We set the export interval of the metrics to 100 ms. The default value is 1 minute.
41+
})
42+
class OtelSpringStarterSmokeTest {
43+
44+
public static final InMemoryMetricExporter METRIC_EXPORTER =
45+
InMemoryMetricExporter.create(AggregationTemporality.DELTA);
46+
private static final InMemoryLogRecordExporter LOG_RECORD_EXPORTER =
47+
InMemoryLogRecordExporter.create();
48+
public static final InMemorySpanExporter SPAN_EXPORTER = InMemorySpanExporter.create();
49+
50+
@Autowired private TestRestTemplate testRestTemplate;
51+
52+
@Configuration(proxyBeanMethods = false)
53+
static class TestConfiguration {
54+
@Bean
55+
public MetricExporter metricExporter() {
56+
return METRIC_EXPORTER;
57+
}
58+
59+
@Bean
60+
public SpanExporter spanExporter() {
61+
return SPAN_EXPORTER;
62+
}
63+
64+
@Bean
65+
public LogRecordExporter logRecordExporter() {
66+
return LOG_RECORD_EXPORTER;
67+
}
68+
}
69+
70+
@Test
71+
void shouldSendTelemetry() throws InterruptedException {
72+
73+
testRestTemplate.getForObject(OtelSpringStarterSmokeTestController.URL, String.class);
74+
75+
Thread.sleep(5_000); // Sleep time could be potentially reduced and perhaps removed with
76+
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8962
77+
// and https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/8963
78+
79+
List<SpanData> exportedSpans = SPAN_EXPORTER.getFinishedSpanItems();
80+
81+
// Span
82+
TracesAssert.assertThat(exportedSpans)
83+
.hasSize(2)
84+
.hasTracesSatisfyingExactly(
85+
traceAssert ->
86+
traceAssert.hasSpansSatisfyingExactly(
87+
spanDataAssert ->
88+
spanDataAssert
89+
.hasKind(SpanKind.CLIENT)
90+
.hasAttribute(
91+
SemanticAttributes.DB_STATEMENT,
92+
"create table test_table (id bigint not null, primary key (id))")),
93+
traceAssert ->
94+
traceAssert.hasSpansSatisfyingExactly(
95+
spanDataAssert ->
96+
spanDataAssert
97+
.hasKind(SpanKind.SERVER)
98+
.hasAttribute(SemanticAttributes.HTTP_METHOD, "GET")
99+
.hasAttribute(SemanticAttributes.HTTP_STATUS_CODE, 200L)
100+
.hasAttribute(SemanticAttributes.HTTP_ROUTE, "/ping")));
101+
102+
// Metric
103+
List<MetricData> exportedMetrics = METRIC_EXPORTER.getFinishedMetricItems();
104+
assertThat(exportedMetrics)
105+
.as("Should contain " + OtelSpringStarterSmokeTestController.TEST_HISTOGRAM + " metric.")
106+
.anySatisfy(
107+
metric -> {
108+
String metricName = metric.getName();
109+
assertThat(metricName).isEqualTo(OtelSpringStarterSmokeTestController.TEST_HISTOGRAM);
110+
});
111+
112+
// Log
113+
List<LogRecordData> logs = LOG_RECORD_EXPORTER.getFinishedLogRecordItems();
114+
LogRecordData firstLog = logs.get(0);
115+
assertThat(firstLog.getBody().asString())
116+
.as("Should instrument logs")
117+
.isEqualTo("Initializing Spring DispatcherServlet 'dispatcherServlet'");
118+
}
119+
}

0 commit comments

Comments
 (0)