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 @@
+
+
+
+