13
13
import io .opentelemetry .context .Scope ;
14
14
import io .opentelemetry .contrib .stacktrace .internal .TestUtils ;
15
15
import io .opentelemetry .sdk .OpenTelemetrySdk ;
16
+ import io .opentelemetry .sdk .autoconfigure .spi .internal .DefaultConfigProperties ;
16
17
import io .opentelemetry .sdk .testing .exporter .InMemorySpanExporter ;
17
18
import io .opentelemetry .sdk .trace .ReadableSpan ;
18
19
import io .opentelemetry .sdk .trace .SpanProcessor ;
19
20
import io .opentelemetry .sdk .trace .data .SpanData ;
20
21
import io .opentelemetry .sdk .trace .export .SimpleSpanProcessor ;
21
22
import io .opentelemetry .semconv .incubating .CodeIncubatingAttributes ;
23
+ import java .time .Duration ;
22
24
import java .time .Instant ;
25
+ import java .util .HashMap ;
23
26
import java .util .List ;
27
+ import java .util .Map ;
24
28
import java .util .function .Consumer ;
25
29
import java .util .function .Function ;
26
30
import java .util .function .Predicate ;
27
- import org .junit .jupiter .api .BeforeEach ;
28
31
import org .junit .jupiter .api .Test ;
29
32
30
33
class StackTraceSpanProcessorTest {
31
34
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 ();
39
37
}
40
38
41
39
@ Test
42
40
void durationAndFiltering () {
41
+ // on duration threshold
42
+ checkSpanWithStackTrace (span -> true , "1ms" , msToNs (1 ));
43
43
// over duration threshold
44
- testSpan (span -> true , 11 , 1 );
44
+ checkSpanWithStackTrace (span -> true , "1ms" , msToNs ( 2 ) );
45
45
// under duration threshold
46
- testSpan (span -> true , 9 , 0 );
46
+ checkSpanWithoutStackTrace (span -> true , "2ms" , msToNs ( 1 ) );
47
47
48
48
// 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 );
50
63
}
51
64
52
65
@ Test
53
66
void spanWithExistingStackTrace () {
54
- testSpan (
67
+ checkSpan (
55
68
span -> true ,
56
- 20 ,
57
- 1 ,
69
+ "1ms" ,
70
+ Duration . ofMillis ( 1 ). toNanos () ,
58
71
sb -> sb .setAttribute (CodeIncubatingAttributes .CODE_STACKTRACE , "hello" ),
59
72
stacktrace -> assertThat (stacktrace ).isEqualTo ("hello" ));
60
73
}
61
74
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 (
65
78
filterPredicate ,
79
+ configString ,
66
80
spanDurationNanos ,
67
- expectedSpansCount ,
68
81
Function .identity (),
69
82
(stackTrace ) ->
70
83
assertThat (stackTrace )
71
84
.describedAs ("span stack trace should contain caller class name" )
72
85
.contains (StackTraceSpanProcessorTest .class .getCanonicalName ()));
73
86
}
74
87
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 (
76
99
Predicate <ReadableSpan > filterPredicate ,
100
+ String configString ,
77
101
long spanDurationNanos ,
78
- int expectedSpansCount ,
79
102
Function <SpanBuilder , SpanBuilder > customizeSpanBuilder ,
80
103
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
+
81
114
try (SpanProcessor processor =
82
- new StackTraceSpanProcessor (exportProcessor , 10 , filterPredicate )) {
115
+ new StackTraceSpanProcessor (
116
+ exportProcessor , DefaultConfigProperties .createFromMap (configMap ), filterPredicate )) {
83
117
84
118
OpenTelemetrySdk sdk = TestUtils .sdkWith (processor );
85
119
Tracer tracer = sdk .getTracer ("test" );
@@ -96,14 +130,12 @@ private void testSpan(
96
130
}
97
131
98
132
List <SpanData > finishedSpans = spansExporter .getFinishedSpanItems ();
99
- assertThat (finishedSpans ).hasSize (expectedSpansCount );
133
+ assertThat (finishedSpans ).hasSize (1 );
100
134
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 );
104
137
105
- stackTraceCheck .accept (stackTrace );
106
- }
138
+ stackTraceCheck .accept (stackTrace );
107
139
}
108
140
}
109
141
}
0 commit comments