File Coverage

blib/lib/Sentry/Hub.pm
Criterion Covered Total %
statement 148 170 87.0
branch 11 18 61.1
condition 14 24 58.3
subroutine 29 31 93.5
pod 0 17 0.0
total 202 260 77.6


line stmt bran cond sub pod time code
1             package Sentry::Hub;
2 6     6   1213178 use Mojo::Base -base, -signatures;
  6         30  
  6         46  
3              
4 6     6   1938 use Mojo::Util 'dumper';
  6         12  
  6         334  
5 6     6   2184 use Sentry::Hub::Scope;
  6         18  
  6         34  
6 6     6   3392 use Sentry::Logger 'logger';
  6         19  
  6         410  
7 6     6   40 use Sentry::Severity;
  6         11  
  6         39  
8 6     6   3367 use Sentry::Tracing::SamplingMethod;
  6         20  
  6         43  
9 6     6   246 use Sentry::Tracing::Transaction;
  6         10  
  6         57  
10 6     6   182 use Sentry::Util qw(uuid4);
  6         10  
  6         346  
11 6     6   31 use Time::HiRes qw(time);
  6         10  
  6         38  
12 6     6   3936 use Try::Tiny;
  6         9259  
  6         11597  
13              
14             my $Instance;
15              
16             has _last_event_id => undef;
17             has _stack => sub { [{}] };
18             has client => undef;
19             has scopes => sub { [Sentry::Hub::Scope->new] };
20              
21 0     0 0 0 sub init ($package, $options) {
  0         0  
  0         0  
  0         0  
22 0         0 $Instance = Sentry::Hub->new($options);
23             }
24              
25 18     18 0 54 sub reset ($self) {
  18         34  
  18         18  
26 18         138 $self->scopes([Sentry::Hub::Scope->new]);
27             }
28              
29 22     22 0 30 sub bind_client ($self, $client) {
  22         62  
  22         33  
  22         29  
30 22         93 $self->client($client);
31 22 100       632 $client->setup_integrations() if $client;
32             }
33              
34 103     103 0 1252 sub get_current_scope ($package) {
  103         140  
  103         126  
35 103         142 return @{ $package->get_current_hub()->scopes }[-1];
  103         206  
36             }
37              
38             sub get_current_hub {
39 243   66 243 0 407716 $Instance //= Sentry::Hub->new();
40 243         659 return $Instance;
41             }
42              
43 37     37 0 59 sub configure_scope ($self, $cb) {
  37         45  
  37         48  
  37         48  
44 37         92 $cb->($self->get_current_scope);
45             }
46              
47 15     15 0 20 sub push_scope ($self) {
  15         23  
  15         20  
48 15         38 my $scope = $self->get_current_scope->clone;
49 15         51 push $self->scopes->@*, $scope;
50 15         107 return $scope;
51             }
52 15     15 0 29 sub pop_scope ($self) { pop @{ $self->scopes } }
  15         30  
  15         22  
  15         19  
  15         47  
53              
54 9     9 0 15 sub with_scope ($self, $cb) {
  9         13  
  9         15  
  9         36  
55 9         32 my $scope = $self->push_scope;
56              
57             try {
58 9     9   536 $cb->($scope);
59             } finally {
60 9     9   341 $self->pop_scope;
61 9         132 };
62             }
63              
64 6     6 0 66 sub get_scope ($self) {
  6         10  
  6         16  
65 6         19 return $self->get_current_scope;
66             }
67              
68 24     24   43 sub _invoke_client ($self, $method, @args) {
  24         32  
  24         40  
  24         49  
  24         53  
69 24 100       63 my $client = $self->client or return;
70 23         180 my $scope = $self->get_current_scope;
71              
72 23 50       221 if ($client->can($method)) {
73 23         93 $client->$method(@args, $scope);
74             } else {
75 0         0 warn "Unknown method: $method";
76             }
77             }
78              
79 24     24   35 sub _new_event_id ($self) {
  24         34  
  24         28  
80 24         97 $self->_last_event_id(uuid4());
81 24         174 return $self->_last_event_id;
82             }
83              
84 10     10 0 18 sub capture_message ($self, $message, $level = undef, $hint = undef,) {
  10         16  
  10         17  
  10         16  
  10         16  
  10         14  
85 10   66     93 $level //= Sentry::Severity->Info;
86 10         29 my $event_id = $self->_new_event_id();
87              
88 10   50     154 $self->_invoke_client('capture_message', $message, $level,
89             { ($hint // {})->%*, event_id => $event_id });
90              
91 10         81 return $event_id;
92             }
93              
94 3     3 0 4 sub capture_exception ($self, $exception, $hint = undef) {
  3         5  
  3         6  
  3         7  
  3         4  
95 3   50     8 $hint //= {};
96 3         12 my $event_id = $self->_new_event_id();
97              
98 3         21 $hint->{original_exception} = $exception;
99              
100 3         23 $self->_invoke_client('capture_exception', $exception,
101             { $hint->%*, event_id => $event_id });
102              
103 3         35 return $event_id;
104             }
105              
106 11     11 0 78 sub capture_event ($self, $event, $hint = undef) {
  11         17  
  11         13  
  11         20  
  11         30  
107 11         26 my $event_id = $self->_new_event_id();
108              
109 11   100     116 $self->_invoke_client('capture_event', $event,
110             { ($hint // {})->%*, event_id => $event_id });
111              
112 11         54 return $event_id;
113             }
114              
115 9     9 0 991 sub add_breadcrumb ($self, $crumb, $hint = undef) {
  9         16  
  9         12  
  9         15  
  9         12  
116 9         24 $self->get_current_scope->add_breadcrumb($crumb);
117             }
118              
119 0     0 0 0 sub run ($self, $cb) {
  0         0  
  0         0  
  0         0  
120 0         0 $cb->($self);
121             }
122              
123 11     11 0 15 sub sample ($self, $transaction, $sampling_context) {
  11         18  
  11         18  
  11         12  
  11         17  
124 11 50       37 my $client = $self->client or return;
125 11   33     107 my $options = ($client && $client->get_options) // {};
      50        
126              
127             # nothing to do if there's no client or if tracing is disabled
128 11 50 33     110 if (!$client || !$options->{traces_sample_rate}) {
129 0         0 $transaction->sampled(0);
130 0         0 return $transaction;
131             }
132              
133             # if the user has forced a sampling decision by passing a `sampled` value in
134             # their transaction context, go with that
135 11 50       37 if (defined $transaction->sampled) {
136 0         0 $transaction->tags({
137             $transaction->tags->%*,
138             __sentry_samplingMethod => Sentry::Tracing::SamplingMethod->Explicit,
139             });
140              
141 0         0 return $transaction;
142             }
143              
144 11         71 my $sample_rate;
145              
146 11 50       30 if (defined $sampling_context->{parent_sampled}) {
147 0         0 $sample_rate = $sampling_context->{parent_sampled};
148 0         0 $transaction->tags({
149             $transaction->tags->%*,
150             __sentry_samplingMethod => Sentry::Tracing::SamplingMethod->Inheritance,
151             });
152             } else {
153 11         21 $sample_rate = $options->{traces_sample_rate};
154 11         51 $transaction->tags({
155             $transaction->tags->%*,
156             __sentry_samplingMethod => Sentry::Tracing::SamplingMethod->Rate,
157             __sentry_sampleRate => $sample_rate,
158             });
159             }
160              
161 11 50       80 if (!$sample_rate) {
162 0         0 logger->log(
163             'Discarding transaction because a negative sampling decision was inherited or tracesSampleRate is set to 0',
164             'Tracing'
165             );
166 0         0 $transaction->sampled(0);
167 0         0 return $transaction;
168             }
169              
170             # Now we roll the dice. Math.random is inclusive of 0, but not of 1, so
171             # strict < is safe here. In case sampleRate is a boolean, the < comparison
172             # will cause it to be automatically cast to 1 if it's true and 0 if it's
173             # false.
174 11         48 $transaction->sampled(rand() < $sample_rate);
175              
176             # if we're not going to keep it, we're done
177 11 50       142 if (!$transaction->sampled) {
178 0         0 logger->log(
179             "Discarding transaction because it's not included in the random sample (sampling rate = $sample_rate)",
180             'Tracing',
181             );
182 0         0 return $transaction;
183             }
184              
185             logger->log(
186 11   50     81 sprintf(
187             'Starting %s transaction - %s',
188             $transaction->op // '(unknown op)',
189             $transaction->name
190             ),
191             'Tracing',
192             );
193 11         164 return $transaction;
194             }
195              
196 11     11 0 101 sub start_transaction ($self, $context, $custom_sampling_context = {}) {
  11         18  
  11         20  
  11         113  
  11         18  
197 11         201 my $transaction = Sentry::Tracing::Transaction->new(
198             { $context->%*, _hub => $self, start_timestamp => time });
199              
200             return $self->sample(
201             $transaction,
202             {
203             parent_sampled => $context->{parent_sampled},
204 11   100     233 ($custom_sampling_context // {})->%*,
205             }
206             );
207             }
208              
209             1;