1313import io .opentelemetry .context .Scope ;
1414import io .opentelemetry .contrib .stacktrace .internal .TestUtils ;
1515import io .opentelemetry .sdk .OpenTelemetrySdk ;
16+ import io .opentelemetry .sdk .autoconfigure .spi .internal .DefaultConfigProperties ;
1617import io .opentelemetry .sdk .testing .exporter .InMemorySpanExporter ;
1718import io .opentelemetry .sdk .trace .ReadableSpan ;
1819import io .opentelemetry .sdk .trace .SpanProcessor ;
1920import io .opentelemetry .sdk .trace .data .SpanData ;
2021import io .opentelemetry .sdk .trace .export .SimpleSpanProcessor ;
2122import io .opentelemetry .semconv .incubating .CodeIncubatingAttributes ;
23+ import java .time .Duration ;
2224import java .time .Instant ;
25+ import java .util .HashMap ;
2326import java .util .List ;
27+ import java .util .Map ;
2428import java .util .function .Consumer ;
2529import java .util .function .Function ;
2630import java .util .function .Predicate ;
27- import org .junit .jupiter .api .BeforeEach ;
2831import org .junit .jupiter .api .Test ;
2932
3033class StackTraceSpanProcessorTest {
3134
32- private InMemorySpanExporter spansExporter ;
33- private SpanProcessor exportProcessor ;
34-
35- @ BeforeEach
36- public void setup () {
37- spansExporter = InMemorySpanExporter .create ();
38- exportProcessor = SimpleSpanProcessor .create (spansExporter );
35+ private static long msToNs (int ms ) {
36+ return Duration .ofMillis (ms ).toNanos ();
3937 }
4038
4139 @ Test
4240 void durationAndFiltering () {
41+ // on duration threshold
42+ checkSpanWithStackTrace (span -> true , "1ms" , msToNs (1 ));
4343 // over duration threshold
44- testSpan (span -> true , 11 , 1 );
44+ checkSpanWithStackTrace (span -> true , "1ms" , msToNs ( 2 ) );
4545 // under duration threshold
46- testSpan (span -> true , 9 , 0 );
46+ checkSpanWithoutStackTrace (span -> true , "2ms" , msToNs ( 1 ) );
4747
4848 // filtering out span
49- testSpan (span -> false , 20 , 0 );
49+ checkSpanWithoutStackTrace (span -> false , "1ms" , 20 );
50+ }
51+
52+ @ Test
53+ void defaultConfig () {
54+ long expectedDefault = msToNs (5 );
55+ checkSpanWithStackTrace (span -> true , null , expectedDefault );
56+ checkSpanWithStackTrace (span -> true , null , expectedDefault + 1 );
57+ checkSpanWithoutStackTrace (span -> true , null , expectedDefault - 1 );
58+ }
59+
60+ @ Test
61+ void disabledConfig () {
62+ checkSpanWithoutStackTrace (span -> true , "-1" , 5 );
5063 }
5164
5265 @ Test
5366 void spanWithExistingStackTrace () {
54- testSpan (
67+ checkSpan (
5568 span -> true ,
56- 20 ,
57- 1 ,
69+ "1ms" ,
70+ Duration . ofMillis ( 1 ). toNanos () ,
5871 sb -> sb .setAttribute (CodeIncubatingAttributes .CODE_STACKTRACE , "hello" ),
5972 stacktrace -> assertThat (stacktrace ).isEqualTo ("hello" ));
6073 }
6174
62- private void testSpan (
63- Predicate <ReadableSpan > filterPredicate , long spanDurationNanos , int expectedSpansCount ) {
64- testSpan (
75+ private static void checkSpanWithStackTrace (
76+ Predicate <ReadableSpan > filterPredicate , String configString , long spanDurationNanos ) {
77+ checkSpan (
6578 filterPredicate ,
79+ configString ,
6680 spanDurationNanos ,
67- expectedSpansCount ,
6881 Function .identity (),
6982 (stackTrace ) ->
7083 assertThat (stackTrace )
7184 .describedAs ("span stack trace should contain caller class name" )
7285 .contains (StackTraceSpanProcessorTest .class .getCanonicalName ()));
7386 }
7487
75- private void testSpan (
88+ private static void checkSpanWithoutStackTrace (
89+ Predicate <ReadableSpan > filterPredicate , String configString , long spanDurationNanos ) {
90+ checkSpan (
91+ filterPredicate ,
92+ configString ,
93+ spanDurationNanos ,
94+ Function .identity (),
95+ (stackTrace ) -> assertThat (stackTrace ).describedAs ("no stack trace expected" ).isNull ());
96+ }
97+
98+ private static void checkSpan (
7699 Predicate <ReadableSpan > filterPredicate ,
100+ String configString ,
77101 long spanDurationNanos ,
78- int expectedSpansCount ,
79102 Function <SpanBuilder , SpanBuilder > customizeSpanBuilder ,
80103 Consumer <String > stackTraceCheck ) {
104+
105+ // they must be re-created as they are shutdown when the span processor is closed
106+ InMemorySpanExporter spansExporter = InMemorySpanExporter .create ();
107+ SpanProcessor exportProcessor = SimpleSpanProcessor .create (spansExporter );
108+
109+ Map <String , String > configMap = new HashMap <>();
110+ if (configString != null ) {
111+ configMap .put ("otel.java.experimental.span-stacktrace.min.duration" , configString );
112+ }
113+
81114 try (SpanProcessor processor =
82- new StackTraceSpanProcessor (exportProcessor , 10 , filterPredicate )) {
115+ new StackTraceSpanProcessor (
116+ exportProcessor , DefaultConfigProperties .createFromMap (configMap ), filterPredicate )) {
83117
84118 OpenTelemetrySdk sdk = TestUtils .sdkWith (processor );
85119 Tracer tracer = sdk .getTracer ("test" );
@@ -96,14 +130,12 @@ private void testSpan(
96130 }
97131
98132 List <SpanData > finishedSpans = spansExporter .getFinishedSpanItems ();
99- assertThat (finishedSpans ).hasSize (expectedSpansCount );
133+ assertThat (finishedSpans ).hasSize (1 );
100134
101- if (!finishedSpans .isEmpty ()) {
102- String stackTrace =
103- finishedSpans .get (0 ).getAttributes ().get (CodeIncubatingAttributes .CODE_STACKTRACE );
135+ String stackTrace =
136+ finishedSpans .get (0 ).getAttributes ().get (CodeIncubatingAttributes .CODE_STACKTRACE );
104137
105- stackTraceCheck .accept (stackTrace );
106- }
138+ stackTraceCheck .accept (stackTrace );
107139 }
108140 }
109141}
0 commit comments