diff --git a/integration_tests/it_exemplars_micrometer_tracing/pom.xml b/integration_tests/it_exemplars_micrometer_tracing/pom.xml new file mode 100644 index 000000000..a91f45c9f --- /dev/null +++ b/integration_tests/it_exemplars_micrometer_tracing/pom.xml @@ -0,0 +1,99 @@ + + + 4.0.0 + + + io.prometheus + integration_tests + 0.16.1-SNAPSHOT + + + it_exemplars_micrometer_tracing + Integration Tests - Exemplars with Micrometer Tracing + + + + io.prometheus + simpleclient + ${project.version} + + + io.micrometer + micrometer-tracing-test + 1.0.0-M7 + + + io.prometheus + simpleclient_httpserver + ${project.version} + + + io.prometheus + it_common + test-jar + ${project.version} + test + + + com.squareup.okhttp3 + okhttp + test + + + org.testcontainers + testcontainers + test + + + ch.qos.logback + logback-classic + test + + + + + + spring-milestone + https://repo.spring.io/milestone + + + + + example-micrometer-tracing-app + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-dependencies + package + + copy-dependencies + + + + + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + diff --git a/integration_tests/it_exemplars_micrometer_tracing/src/main/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExampleApplication.java b/integration_tests/it_exemplars_micrometer_tracing/src/main/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExampleApplication.java new file mode 100644 index 000000000..0ea43e817 --- /dev/null +++ b/integration_tests/it_exemplars_micrometer_tracing/src/main/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExampleApplication.java @@ -0,0 +1,53 @@ +package io.prometheus.client.it.exemplars_micrometer_tracing; + +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.test.simple.SimpleSpan; +import io.micrometer.tracing.test.simple.SimpleTracer; +import io.prometheus.client.Counter; +import io.prometheus.client.exemplars.DefaultExemplarSampler; +import io.prometheus.client.exemplars.ExemplarConfig; +import io.prometheus.client.exemplars.ExemplarSampler; +import io.prometheus.client.exemplars.tracer.micrometer.tracing.MicrometerTracingSpanContextSupplier; +import io.prometheus.client.exporter.HTTPServer; + +import java.io.IOException; + +/** + * Example application using Micrometer Tracing. + */ +public class ExampleApplication { + + public static void main(String[] args) throws IOException, InterruptedException { + SimpleTracer tracer = new SimpleTracer(); + ExemplarSampler exemplarSampler = new DefaultExemplarSampler(new MicrometerTracingSpanContextSupplier(tracer)); + ExemplarConfig.setCounterExemplarSampler(exemplarSampler); + ExemplarConfig.setHistogramExemplarSampler(exemplarSampler); + ExemplarConfig.enableExemplars(); + + Counter counter = Counter.build() + .name("test") + .help("help") + .register(); + + Span span = nextSpan(tracer); + try (Tracer.SpanInScope ws = tracer.withSpan(span.start())) { + counter.inc(1); + } + finally { + span.end(); + } + + new HTTPServer(9000); + Thread.currentThread().join(); // sleep forever + } + + private static Span nextSpan(SimpleTracer tracer) { + SimpleSpan span = tracer.nextSpan().name("testSpan"); + span.context().setSampled(true); + span.context().setTraceId("45e09ee1c39e1f8f"); + span.context().setSpanId("22f69a0e2c0ab635"); + + return span; + } +} diff --git a/integration_tests/it_exemplars_micrometer_tracing/src/test/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExemplarsMicrometerTracingIT.java b/integration_tests/it_exemplars_micrometer_tracing/src/test/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExemplarsMicrometerTracingIT.java new file mode 100644 index 000000000..9b5751b71 --- /dev/null +++ b/integration_tests/it_exemplars_micrometer_tracing/src/test/java/io/prometheus/client/it/exemplars_micrometer_tracing/ExemplarsMicrometerTracingIT.java @@ -0,0 +1,70 @@ +package io.prometheus.client.it.exemplars_micrometer_tracing; + +import io.prometheus.client.it.common.LogConsumer; +import io.prometheus.client.it.common.Scraper; +import io.prometheus.client.it.common.Volume; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.testcontainers.containers.BindMode; +import org.testcontainers.containers.GenericContainer; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; + +/** + * Test if traces from Micrometer Tracing are picked up as Exemplars. + **/ +public class ExemplarsMicrometerTracingIT { + + private Volume volume; + private GenericContainer javaContainer; + + private final String appJar = "example-micrometer-tracing-app.jar"; + private final String image = "openjdk:11-jre"; + private final String[] cmd = new String[] {"java", "-cp", appJar + ":dependency/*", ExampleApplication.class.getName()}; + + @Before + public void setUp() throws IOException, URISyntaxException { + volume = Volume.create("exemplars-micrometer-tracing-test") + .copyFromTargetDirectory(appJar) + .copyFromTargetDirectory("dependency"); + javaContainer = new GenericContainer<>(image) + .withFileSystemBind(volume.getHostPath(), "/app", BindMode.READ_ONLY) + .withWorkingDirectory("/app") + .withLogConsumer(LogConsumer.withPrefix(image)) + .withExposedPorts(9000) + .withCommand(cmd); + System.out.println("Java image: " + image); + System.out.println("Temp dir: " + volume.getHostPath()); + System.out.println("cmd: " + String.join(" ", cmd)); + } + + @After + public void tearDown() throws IOException { + javaContainer.stop(); + volume.remove(); + } + + @Test + public void exemplarsShouldBeFound() { + javaContainer.start(); + List metrics = Scraper.scrape("http://localhost:" + javaContainer.getMappedPort(9000) + "/metrics", 10_000); + boolean testTotalWithExemplarFound = false; + boolean testTotalWithoutExemplarFound = false; + for (String metric : metrics) { + System.out.println(metric); + if (metric.matches("^test_total 1\\.0 # \\{span_id=\"22f69a0e2c0ab635\",trace_id=\"45e09ee1c39e1f8f\"} 1.0 [0-9.]+$")) { + testTotalWithExemplarFound = true; + } + if (metric.matches("^test_total 1\\.0$")) { + testTotalWithoutExemplarFound = true; + } + } + + Assert.assertTrue("test_total metric with exemplars expected", testTotalWithExemplarFound); + Assert.assertFalse("test_total without exemplar should not be there", testTotalWithoutExemplarFound); + } +} diff --git a/integration_tests/it_exemplars_micrometer_tracing/src/test/resources/logback-test.xml b/integration_tests/it_exemplars_micrometer_tracing/src/test/resources/logback-test.xml new file mode 100644 index 000000000..e8c716f5e --- /dev/null +++ b/integration_tests/it_exemplars_micrometer_tracing/src/test/resources/logback-test.xml @@ -0,0 +1,14 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n + + + + + + + + + + diff --git a/integration_tests/it_exemplars_micrometer_tracing/version-rules.xml b/integration_tests/it_exemplars_micrometer_tracing/version-rules.xml new file mode 100644 index 000000000..5c6d39593 --- /dev/null +++ b/integration_tests/it_exemplars_micrometer_tracing/version-rules.xml @@ -0,0 +1,6 @@ + + + + diff --git a/integration_tests/it_exemplars_otel_agent/pom.xml b/integration_tests/it_exemplars_otel_agent/pom.xml index 5ec4b0727..a65db1ec0 100644 --- a/integration_tests/it_exemplars_otel_agent/pom.xml +++ b/integration_tests/it_exemplars_otel_agent/pom.xml @@ -76,6 +76,7 @@ org.springframework.boot spring-boot-maven-plugin + 2.6.3 diff --git a/integration_tests/pom.xml b/integration_tests/pom.xml index 80932482a..54128e073 100644 --- a/integration_tests/pom.xml +++ b/integration_tests/pom.xml @@ -22,6 +22,7 @@ + it_exemplars_micrometer_tracing it_exemplars_otel_sdk it_exemplars_otel_agent it_java_versions diff --git a/simpleclient/pom.xml b/simpleclient/pom.xml index 9e1a70c49..df08e18ee 100644 --- a/simpleclient/pom.xml +++ b/simpleclient/pom.xml @@ -37,6 +37,11 @@ + + io.prometheus + simpleclient_tracer_micrometer_tracing + ${project.version} + io.prometheus simpleclient_tracer_otel diff --git a/simpleclient_tracer/pom.xml b/simpleclient_tracer/pom.xml index 59e0c35e2..90efefadd 100644 --- a/simpleclient_tracer/pom.xml +++ b/simpleclient_tracer/pom.xml @@ -20,9 +20,23 @@ opentelemetry-api 1.15.0 + + io.micrometer + micrometer-tracing-bom + 1.0.0-M7 + pom + import + + + + spring-milestone + https://repo.spring.io/milestone + + + The Apache Software License, Version 2.0 @@ -33,6 +47,7 @@ simpleclient_tracer_common + simpleclient_tracer_micrometer_tracing simpleclient_tracer_otel simpleclient_tracer_otel_agent diff --git a/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/pom.xml b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/pom.xml new file mode 100644 index 000000000..aacc8403b --- /dev/null +++ b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + + io.prometheus + simpleclient_tracer + 0.16.1-SNAPSHOT + + + simpleclient_tracer_micrometer_tracing + bundle + + Prometheus Java Span Context Supplier - Micrometer Tracing + + + + io.prometheus + simpleclient_tracer_common + ${project.version} + + + io.micrometer + micrometer-tracing + 1.0.0-M7 + provided + true + + + junit + junit + 4.13.2 + test + + + org.mockito + mockito-core + 4.6.1 + test + + + org.assertj + assertj-core + 3.23.1 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + diff --git a/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/main/java/io/prometheus/client/exemplars/tracer/micrometer.tracing/MicrometerTracingSpanContextSupplier.java b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/main/java/io/prometheus/client/exemplars/tracer/micrometer.tracing/MicrometerTracingSpanContextSupplier.java new file mode 100644 index 000000000..1e2906c68 --- /dev/null +++ b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/main/java/io/prometheus/client/exemplars/tracer/micrometer.tracing/MicrometerTracingSpanContextSupplier.java @@ -0,0 +1,31 @@ +package io.prometheus.client.exemplars.tracer.micrometer.tracing; + +import io.micrometer.tracing.Span; +import io.micrometer.tracing.Tracer; +import io.prometheus.client.exemplars.tracer.common.SpanContextSupplier; + +public class MicrometerTracingSpanContextSupplier implements SpanContextSupplier { + private final Tracer tracer; + + public MicrometerTracingSpanContextSupplier(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public String getTraceId() { + // assuming isSampled is called before calling this + return tracer.currentSpan().context().traceId(); + } + + @Override + public String getSpanId() { + // assuming isSampled is called before calling this + return tracer.currentSpan().context().spanId(); + } + + @Override + public boolean isSampled() { + Span span = tracer.currentSpan(); + return span != null ? span.context().sampled() : false; + } +} diff --git a/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/test/java/io/prometheus/client/exemplars/tracer/micrometer/tracing/MicrometerTracingSpanContextSupplierTest.java b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/test/java/io/prometheus/client/exemplars/tracer/micrometer/tracing/MicrometerTracingSpanContextSupplierTest.java new file mode 100644 index 000000000..786f36923 --- /dev/null +++ b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/src/test/java/io/prometheus/client/exemplars/tracer/micrometer/tracing/MicrometerTracingSpanContextSupplierTest.java @@ -0,0 +1,60 @@ +package io.prometheus.client.exemplars.tracer.micrometer.tracing; + +import io.micrometer.tracing.Span; +import io.micrometer.tracing.TraceContext; +import io.micrometer.tracing.Tracer; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class MicrometerTracingSpanContextSupplierTest { + + @Mock private Tracer tracer; + @Mock private Span span; + @Mock private TraceContext traceContext; + @InjectMocks private MicrometerTracingSpanContextSupplier spanContextSupplier; + + @Test + public void nullSpanShouldBeReportedAsNotSampled() { + when(tracer.currentSpan()).thenReturn(null); + assertThat(spanContextSupplier.isSampled()).isFalse(); + } + + @Test + public void notSampledSpanShouldBeReportedAsNotSampled() { + when(tracer.currentSpan()).thenReturn(span); + when(span.context()).thenReturn(traceContext); + when(traceContext.sampled()).thenReturn(false); + + assertThat(spanContextSupplier.isSampled()).isFalse(); + } + + @Test + public void sampledSpanShouldBeReportedAsSampled() { + when(tracer.currentSpan()).thenReturn(span); + when(span.context()).thenReturn(traceContext); + when(traceContext.sampled()).thenReturn(true); + + assertThat(spanContextSupplier.isSampled()).isTrue(); + } + + @Test + public void traceIdAndSpanIdShouldBeFetchedFromTheSpan() { + String traceId = "9f013d8df008f901"; + String spanId = "04f1747a5f4cde87"; + + when(tracer.currentSpan()).thenReturn(span); + when(span.context()).thenReturn(traceContext); + when(traceContext.traceId()).thenReturn(traceId); + when(traceContext.spanId()).thenReturn(spanId); + + assertThat(spanContextSupplier.getTraceId()).isSameAs(traceId); + assertThat(spanContextSupplier.getSpanId()).isSameAs(spanId); + } +} diff --git a/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/version-rules.xml b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/version-rules.xml new file mode 100644 index 000000000..5c6d39593 --- /dev/null +++ b/simpleclient_tracer/simpleclient_tracer_micrometer_tracing/version-rules.xml @@ -0,0 +1,6 @@ + + + +