File Coverage

blib/lib/OpenTelemetry/SDK/Trace/Span.pm
Criterion Covered Total %
statement 114 116 98.2
branch 17 20 85.0
condition 12 16 75.0
subroutine 24 24 100.0
pod 8 8 100.0
total 175 184 95.1


line stmt bran cond sub pod time code
1 5     5   221323 use Object::Pad ':experimental( init_expr mop )';
  5         9262  
  5         52  
2              
3             package OpenTelemetry::SDK::Trace::Span;
4              
5             our $VERSION = '0.028';
6              
7 5     5   1393 use OpenTelemetry::Attributes;
  5         45233  
  5         1114  
8              
9             class OpenTelemetry::SDK::Trace::Span
10             :isa(OpenTelemetry::Trace::Span)
11             :does(OpenTelemetry::Attributes)
12 2     2   745 {
  2         12995  
  2         232  
13 5     5   437 use List::Util qw( any pairs );
  5         8  
  5         425  
14 5     5   26 use Ref::Util qw( is_arrayref is_hashref );
  5         8  
  5         225  
15 5     5   24 use Time::HiRes 'time';
  5         7  
  5         41  
16              
17             use OpenTelemetry::Constants
18 5     5   715 -span_kind => { -as => sub { shift =~ s/^SPAN_KIND_//r } };
  5         7  
  5         86  
  25         5367  
19              
20 5     5   450 use OpenTelemetry::Common ();
  5         10  
  5         155  
21 5     5   3275 use OpenTelemetry::SDK::Trace::SpanLimits;
  5         12  
  5         267  
22 5     5   2474 use OpenTelemetry::SDK::Trace::Span::Readable;
  5         15  
  5         245  
23 5     5   2585 use OpenTelemetry::Trace::Event;
  5         4933  
  5         227  
24 5     5   2277 use OpenTelemetry::Trace::Link;
  5         26461  
  5         211  
25 5     5   35 use OpenTelemetry::Trace::SpanContext;
  5         10  
  5         112  
26 5     5   2406 use OpenTelemetry::Trace::Span::Status;
  5         12665  
  5         286  
27 5     5   742 use OpenTelemetry::Trace;
  5         23514  
  5         269  
28              
29 5         181 use isa qw(
30             Exception::Base
31             Exception::Class::Base
32             OpenTelemetry::Trace::SpanContext
33 5     5   32 );
  5         9  
34              
35             my $logger = OpenTelemetry::Common::internal_logger;
36              
37             field $dropped_events = 0;
38             field $dropped_links = 0;
39             field $end;
40             field $kind :param = INTERNAL;
41             field $limits :param //= OpenTelemetry::SDK::Trace::SpanLimits->new;
42             field $name :param;
43             field $resource :param = undef;
44             field $scope :param;
45             field $start :param = undef;
46             field $status = OpenTelemetry::Trace::Span::Status->unset;
47             field @events;
48             field @links;
49             field @processors;
50             field $parent_span_context;
51              
52             # Internal method for adding a single link
53             #
54             # $self->$add_link({
55             # context => $valid_span_context,
56             # attributes => \%link_attributes,
57             # })
58             #
59             # Links with invalid span contexts are ignored
60             #
61             method $add_link ( $args ) {
62             return unless isa_OpenTelemetry_Trace_SpanContext($args->{context})
63             && $args->{context}->valid;
64              
65             if ( scalar @links >= $limits->link_count_limit ) {
66             $dropped_links++;
67             $logger->warn('Dropped link because it would exceed specified limit');
68             return;
69             }
70              
71             push @links, OpenTelemetry::Trace::Link->new(
72             context => $args->{context},
73             attributes => $args->{attributes},
74             attribute_count_limit => $limits->link_attribute_count_limit,
75             );
76             }
77              
78             ADJUSTPARAMS ( $params ) {
79             my $now = time;
80             undef $start if $start && $start > $now;
81             $start //= $now;
82              
83             $kind = INTERNAL if $kind < INTERNAL || $kind > CONSUMER;
84              
85             @processors = @{ delete $params->{processors} // [] };
86              
87             $self->$add_link($_) for @{ delete $params->{links} // [] };
88              
89             my $parent = delete $params->{parent};
90              
91             $parent_span_context
92             = OpenTelemetry::Trace->span_from_context($parent)->context;
93              
94             $_->on_start( $self, $parent ) for @processors;
95             }
96              
97 4     4 1 2724 method set_name ( $new ) {
  4         16  
  4         9  
  4         7  
98 4 100 100     13 return $self unless $self->recording && $new;
99              
100 1         3 $name = $new;
101              
102 1         9 $self;
103             }
104              
105 2     2 1 729 method set_attribute ( %new ) {
  2         8  
  2         7  
  2         4  
106 2 100       7 unless ( $self->recording ) {
107 1         7 $logger->warn('Attempted to set attributes on a span that is not recording');
108 1         82 return $self
109             }
110              
111             # FIXME: Ideally an overridable method from role, but that is not supported
112 1         7 Object::Pad::MOP::Class->for_class('OpenTelemetry::Attributes')
113             ->get_field('$attributes')
114             ->value($self)
115             ->set(%new);
116              
117 1         187 $self;
118             }
119              
120 6     6 1 11023 method set_status ( $new, $description = undef ) {
  6         26  
  6         11  
  6         13  
  6         11  
121 6 100 100     17 return $self if !$self->recording || $status->is_ok;
122              
123 4   100     98 my $value = OpenTelemetry::Trace::Span::Status->new(
124             code => $new,
125             description => $description // '',
126             );
127              
128 4 100       79 $status = $value unless $value->is_unset;
129              
130 4         58 $self;
131             }
132              
133 9     9 1 738 method add_event (%args) {
  9         29  
  9         28  
  9         19  
134 9 100       26 return $self unless $self->recording;
135              
136 8 100       42 if ( scalar @events >= $limits->event_count_limit ) {
137 1         5 $dropped_events++;
138 1         7 $logger->warn('Dropped event because it would exceed specified limit');
139 1         101 return $self;
140             }
141              
142             push @events, OpenTelemetry::Trace::Event->new(
143             name => $args{name},
144             timestamp => $args{timestamp},
145             attributes => $args{attributes},
146 7         51 attribute_count_limit => $limits->event_attribute_count_limit,
147             attribute_length_limit => $limits->event_attribute_length_limit,
148             );
149              
150 7         1050 $self;
151             }
152              
153 8     8 1 6458 method end ( $time = undef ) {
  8         33  
  8         19  
  8         17  
154             # This should in theory be an atomic check. For now, to reduce
155             # the chances of it becoming a problem, we check the field
156             # directly instead of going through `recording`
157 8 100       38 return $self if defined $end;
158 7   66     40 $end = $time // time;
159              
160 7         22 $_->on_end($self) for @processors;
161              
162 7         48 $self;
163             }
164              
165 3     3 1 3469 method record_exception ( $exception, %attributes ) {
  3         9  
  3         35  
  3         9  
  3         6  
166 3 50       13 return $self unless $self->recording;
167              
168 3         10 my ( $message, $stacktrace );
169 3 50       20 if ( isa_Exception_Class_Base $exception ) {
170 0         0 $message = $exception->message;
171 0         0 $stacktrace = $exception->trace->as_string;
172             }
173             else {
174             # This should cover the following common exceptions:
175             # * Catalyst::Exception::Basic
176             # * Class::Throwable
177             # * Dancer::Exception::Base
178             # * Exception::Base
179             # * Mojo::Exception
180             # * Throwable::Error
181             # * X::Tiny
182             # * plain die strings
183              
184 3         119 local $ENV{MOJO_EXCEPTION_VERBOSE} = 1;
185 3         9 local $Class::Throwable::DEFAULT_VERBOSITY = 2;
186              
187 3         61 ( $message, $stacktrace ) = split /\n/, "$exception", 2;
188              
189 3 50 0     250 $stacktrace //= $exception->get_caller_stacktrace
190             if isa_Exception_Base $exception;
191             }
192              
193 3   100     35 $self->add_event(
194             name => 'exception',
195             attributes => {
196             'exception.type' => ref $exception || 'string',
197             'exception.message' => $message,
198             'exception.stacktrace' => $stacktrace,
199             %attributes,
200             }
201             );
202             }
203              
204 30     30 1 6029 method recording () { ! defined $end }
  30         93  
  30         50  
  30         182  
205              
206 24     24 1 16767 method snapshot () {
  24         116  
  24         46  
207 24         121 OpenTelemetry::SDK::Trace::Span::Readable->new(
208             attributes => $self->attributes,
209             context => $self->context,
210             dropped_events => $dropped_events,
211             dropped_links => $dropped_links,
212             end_timestamp => $end,
213             events => [ @events ],
214             instrumentation_scope => $scope,
215             kind => $kind,
216             links => [ @links ],
217             name => $name,
218             parent_span_id => $parent_span_context->span_id,
219             resource => $resource,
220             start_timestamp => $start,
221             status => $status,
222             );
223             }
224             }