File Coverage

blib/lib/OpenTelemetry/SDK/Trace/TracerProvider.pm
Criterion Covered Total %
statement 85 106 80.1
branch 8 16 50.0
condition 2 5 40.0
subroutine 23 27 85.1
pod 4 4 100.0
total 122 158 77.2


line stmt bran cond sub pod time code
1 3     3   576713 use Object::Pad ':experimental(init_expr)';
  3         16513  
  3         32  
2             # ABSTRACT: A TracerProvider for the OpenTelemetry SDK
3              
4             package OpenTelemetry::SDK::Trace::TracerProvider;
5              
6             our $VERSION = '0.028';
7              
8 3     3   2614 use OpenTelemetry;
  3         217513  
  3         51  
9              
10             class OpenTelemetry::SDK::Trace::TracerProvider :isa(OpenTelemetry::Trace::TracerProvider) {
11 3     3   1784 use Feature::Compat::Try;
  3         8  
  3         52  
12 3     3   2094 use Future::AsyncAwait;
  3         63213  
  3         26  
13 3     3   265 use List::Util 'any';
  3         5  
  3         300  
14 3     3   32 use Mutex;
  3         6  
  3         98  
15 3     3   15 use OpenTelemetry::Common qw( config timeout_timestamp maybe_timeout );
  3         5  
  3         259  
16 3     3   22 use OpenTelemetry::Constants -trace_export;
  3         21  
  3         40  
17 3     3   4135 use OpenTelemetry::Propagator::TraceContext::TraceFlags;
  3         8  
  3         118  
18 3     3   1686 use OpenTelemetry::SDK::InstrumentationScope;
  3         12  
  3         193  
19 3     3   1639 use OpenTelemetry::SDK::Resource;
  3         7  
  3         160  
20 3     3   1550 use OpenTelemetry::SDK::Trace::Sampler::AlwaysOff;
  3         12  
  3         152  
21 3     3   1560 use OpenTelemetry::SDK::Trace::Sampler::AlwaysOn;
  3         7  
  3         165  
22 3     3   1449 use OpenTelemetry::SDK::Trace::Sampler::ParentBased;
  3         8  
  3         164  
23 3     3   1551 use OpenTelemetry::SDK::Trace::Sampler::TraceIDRatioBased;
  3         14  
  3         248  
24 3     3   2817 use OpenTelemetry::SDK::Trace::Span;
  3         13  
  3         193  
25 3     3   27 use OpenTelemetry::SDK::Trace::SpanLimits;
  3         7  
  3         100  
26 3     3   2029 use OpenTelemetry::SDK::Trace::Tracer;
  3         12  
  3         200  
27 3     3   22 use OpenTelemetry::Trace::SpanContext;
  3         4  
  3         11681  
28              
29             field $sampler :param = undef;
30             field $id_generator :param = 'OpenTelemetry::Trace';
31             field $span_limits :param //= OpenTelemetry::SDK::Trace::SpanLimits->new;
32             field $resource :param //= OpenTelemetry::SDK::Resource->new;
33             field $stopped;
34             field %registry;
35             field @processors;
36              
37             field $lock //= Mutex->new;
38             field $registry_lock //= Mutex->new;
39              
40             my $logger = OpenTelemetry::Common::internal_logger;
41              
42             ADJUST {
43             try {
44             for ( config('TRACES_SAMPLER') // 'parentbased_always_on' ) {
45             $sampler //= OpenTelemetry::SDK::Trace::Sampler::AlwaysOn->new
46             if $_ eq 'always_on';
47              
48             $sampler //= OpenTelemetry::SDK::Trace::Sampler::AlwaysOff->new
49             if $_ eq 'always_off';
50              
51             $sampler //= OpenTelemetry::SDK::Trace::Sampler::TraceIDRatioBased->new(
52             ratio => config('TRACES_SAMPLER_ARG') // 1,
53             ) if $_ eq 'traceidratio';
54              
55             $sampler //= OpenTelemetry::SDK::Trace::Sampler::ParentBased->new(
56             root => OpenTelemetry::SDK::Trace::Sampler::AlwaysOn->new,
57             ) if $_ eq 'parentbased_always_on';
58              
59             $sampler //= OpenTelemetry::SDK::Trace::Sampler::ParentBased->new(
60             root => OpenTelemetry::SDK::Trace::Sampler::AlwaysOff->new,
61             ) if $_ eq 'parentbased_always_off';
62              
63             $sampler //= OpenTelemetry::SDK::Trace::Sampler::ParentBased->new(
64             root => OpenTelemetry::SDK::Trace::Sampler::TraceIDRatioBased->new(
65             ratio => config('TRACES_SAMPLER_ARG') // 1,
66             ),
67             ) if $_ eq 'parentbased_traceidratio';
68             }
69             }
70             catch ($e) {
71             my $default = OpenTelemetry::SDK::Trace::Sampler::ParentBased->new(
72             root => OpenTelemetry::SDK::Trace::Sampler::AlwaysOn->new,
73             );
74              
75             OpenTelemetry->handle_error(
76             exception => $e,
77             message => 'installing default sampler ' . $default->description,
78             );
79              
80             $sampler = $default;
81             }
82             }
83              
84             method $create_span (%args) {
85             my %span = %args{qw( parent name kind start scope links resource )};
86              
87             $span{attribute_count_limit} = $span_limits->attribute_count_limit;
88             $span{attribute_length_limit} = $span_limits->attribute_length_limit;
89              
90             my $parent_span_context = OpenTelemetry::Trace
91             ->span_from_context( $span{parent} )->context;
92              
93             my $trace_id = $parent_span_context->valid
94             ? $parent_span_context->trace_id
95             : $id_generator->generate_trace_id;
96              
97             my $result = $sampler->should_sample(
98             trace_id => $trace_id,
99             context => $args{parent},
100             name => $span{name},
101             kind => $span{kind},
102             attributes => $args{attributes},
103             links => $span{links},
104             );
105              
106             $span{attributes} = {
107             %{ $args{attributes} // {} },
108             %{ $result->attributes },
109             };
110              
111             my $span_id = $id_generator->generate_span_id;
112              
113             if ( $result->recording && !$stopped ) {
114             my $flags = $result->sampled
115             ? OpenTelemetry::Propagator::TraceContext::TraceFlags->new(1)
116             : OpenTelemetry::Propagator::TraceContext::TraceFlags->new(0);
117              
118             my $context = OpenTelemetry::Trace::SpanContext->new(
119             trace_id => $trace_id,
120             span_id => $span_id,
121             trace_flags => $flags,
122             trace_state => $result->trace_state,
123             );
124              
125             $span{context} = $context;
126             $span{processors} = [ @processors ];
127              
128             return OpenTelemetry::SDK::Trace::Span->new(%span);
129             }
130              
131             OpenTelemetry::Trace->non_recording_span(
132             OpenTelemetry::Trace::SpanContext->new(
133             trace_id => $trace_id,
134             span_id => $span_id,
135             trace_state => $result->trace_state,
136             )
137             );
138             }
139              
140 3     3 1 9833 method tracer (%args) {
  3         26  
  3         128  
  3         9  
141 3         7 my %defaults;
142              
143 3         6 $defaults{scope} = do {
144             # If no name is provided, we get it from the caller
145             # This has to override the version, since the version
146             # only makes sense for the name
147 3 100       44 $args{name} || do {
148 1         13 ( $args{name} ) = caller;
149 1         226 $args{version} = $args{name}->VERSION;
150             };
151              
152 3 50       14 unless ( $args{name} ) {
153             $logger->warn(
154             'Invalid name when retrieving tracer. Setting to empty string',
155             { value => $args{name} },
156 0         0 );
157              
158 0   0     0 $args{name} //= '';
159 0         0 delete $args{version};
160             }
161              
162             OpenTelemetry::SDK::InstrumentationScope
163 3         69 ->new( %args{qw( name version attributes )} );
164             };
165              
166             $defaults{resource} = $args{schema_url}
167             ? $resource->merge(
168             OpenTelemetry::SDK::Resource->empty(
169             schema_url => $args{schema_url}
170 3 50       18 ),
171             )
172             : $resource;
173              
174             $registry_lock->enter( sub {
175             my $key
176             = $defaults{scope}->to_string
177             . '-'
178 3     3   166 . $defaults{resource}->schema_url;
179              
180             $registry{$key} //= OpenTelemetry::SDK::Trace::Tracer->new(
181 0         0 span_creator => sub { $self->$create_span( @_, %defaults ) },
182 3   66     77 );
183 3         62 });
184             }
185              
186             method $atomic_call_on_processors ( $method, $timeout ) {
187             my $start = timeout_timestamp;
188              
189             my $result = TRACE_EXPORT_SUCCESS;
190              
191             for my $processor ( @processors ) {
192             my $remaining = maybe_timeout $timeout, $start;
193              
194             if ( $timeout && ! $remaining ) {
195             $result = TRACE_EXPORT_TIMEOUT;
196             last;
197             }
198              
199             my $res = $processor->$method($remaining)->get;
200             $result = $res if $res > $result;
201             }
202              
203              
204             return $result;
205             }
206              
207 0     0 1 0 async method shutdown ( $timeout = undef ) {
  0         0  
  0         0  
  0         0  
  0         0  
208 0 0       0 return TRACE_EXPORT_SUCCESS if $stopped;
209              
210             $lock->enter(
211             sub {
212 0     0   0 $stopped = 1;
213 0         0 $self->$atomic_call_on_processors( shutdown => $timeout );
214             }
215 0         0 );
216             }
217              
218 0     0 1 0 async method force_flush ( $timeout = undef ) {
  0         0  
  0         0  
  0         0  
  0         0  
219 0 0       0 return TRACE_EXPORT_SUCCESS if $stopped;
220              
221             $lock->enter(
222             sub {
223 0     0   0 $self->$atomic_call_on_processors( force_flush => $timeout );
224             }
225 0         0 );
226             }
227              
228 4     4 1 63821 method add_span_processor ($processor) {
  4         138  
  4         47  
  4         57  
229             $lock->enter( sub {
230 4 50   4   699 return $logger
231             ->warn('Attempted to add a span processor to a TraceProvider after shutdown')
232             if $stopped;
233              
234 4 50       101 return $logger
235             ->warn('Attempted to add an object that does not do the OpenTelemetry::Trace::Span::Processor role as a span processor to a TraceProvider')
236             unless $processor->DOES('OpenTelemetry::Trace::Span::Processor');
237              
238 4         50 my %seen = map { ref, 1 } @processors;
  1         5  
239 4         57 my $candidate = ref $processor;
240              
241             return $logger
242 4 100       35 ->warn("Attempted to add a $candidate span processor to a TraceProvider more than once") if $seen{$candidate};
243              
244 3         42 push @processors, $processor;
245 4         457 });
246              
247 4         408 $self;
248             }
249             }