1616
1717package org .springframework .boot .actuate .autoconfigure .tracing .prometheus ;
1818
19+ import java .util .Optional ;
20+ import java .util .regex .Matcher ;
21+ import java .util .regex .Pattern ;
22+
1923import io .micrometer .observation .Observation ;
2024import io .micrometer .observation .ObservationRegistry ;
2125import io .micrometer .prometheus .PrometheusMeterRegistry ;
3337import org .springframework .boot .test .context .runner .ApplicationContextRunner ;
3438import org .springframework .context .annotation .Bean ;
3539import org .springframework .context .annotation .Configuration ;
40+ import org .springframework .util .StringUtils ;
3641
3742import static org .assertj .core .api .Assertions .assertThat ;
3843import static org .mockito .Mockito .mock ;
4449 */
4550class PrometheusExemplarsAutoConfigurationTests {
4651
52+ private static final Pattern BUCKET_TRACE_INFO_PATTERN = Pattern .compile (
53+ "^test_observation_seconds_bucket\\ {error=\" none\" ,le=\" .+\" } 1.0 # \\ {span_id=\" (\\ p{XDigit}+)\" ,trace_id=\" (\\ p{XDigit}+)\" } .+$" );
54+
55+ private static final Pattern COUNTER_TRACE_INFO_PATTERN = Pattern .compile (
56+ "^test_observation_seconds_count\\ {error=\" none\" } 1.0 # \\ {span_id=\" (\\ p{XDigit}+)\" ,trace_id=\" (\\ p{XDigit}+)\" } .+$" );
57+
4758 private final ApplicationContextRunner contextRunner = new ApplicationContextRunner ()
4859 .withPropertyValues ("management.tracing.sampling.probability=1.0" ,
4960 "management.metrics.distribution.percentiles-histogram.all=true" )
@@ -80,9 +91,27 @@ void prometheusOpenMetricsOutputShouldContainExemplars() {
8091 Observation .start ("test.observation" , observationRegistry ).stop ();
8192 PrometheusMeterRegistry prometheusMeterRegistry = context .getBean (PrometheusMeterRegistry .class );
8293 String openMetricsOutput = prometheusMeterRegistry .scrape (TextFormat .CONTENT_TYPE_OPENMETRICS_100 );
83- assertThat (openMetricsOutput ).contains ("test_observation_seconds_bucket" )
84- .containsOnlyOnce ("trace_id=" )
85- .containsOnlyOnce ("span_id=" );
94+
95+ assertThat (openMetricsOutput ).contains ("test_observation_seconds_bucket" );
96+ assertThat (openMetricsOutput ).containsOnlyOnce ("test_observation_seconds_count" );
97+ assertThat (StringUtils .countOccurrencesOf (openMetricsOutput , "span_id" )).isEqualTo (2 );
98+ assertThat (StringUtils .countOccurrencesOf (openMetricsOutput , "trace_id" )).isEqualTo (2 );
99+
100+ Optional <TraceInfo > bucketTraceInfo = openMetricsOutput .lines ()
101+ .filter ((line ) -> line .contains ("test_observation_seconds_bucket" ) && line .contains ("span_id" ))
102+ .map (BUCKET_TRACE_INFO_PATTERN ::matcher )
103+ .flatMap (Matcher ::results )
104+ .map ((matchResult ) -> new TraceInfo (matchResult .group (2 ), matchResult .group (1 )))
105+ .findFirst ();
106+
107+ Optional <TraceInfo > counterTraceInfo = openMetricsOutput .lines ()
108+ .filter ((line ) -> line .contains ("test_observation_seconds_count" ) && line .contains ("span_id" ))
109+ .map (COUNTER_TRACE_INFO_PATTERN ::matcher )
110+ .flatMap (Matcher ::results )
111+ .map ((matchResult ) -> new TraceInfo (matchResult .group (2 ), matchResult .group (1 )))
112+ .findFirst ();
113+
114+ assertThat (bucketTraceInfo ).isNotEmpty ().contains (counterTraceInfo .orElse (null ));
86115 });
87116 }
88117
@@ -98,4 +127,7 @@ SpanContextSupplier customSpanContextSupplier() {
98127
99128 }
100129
130+ private record TraceInfo (String traceId , String spanId ) {
131+ }
132+
101133}
0 commit comments