File Coverage

blib/lib/Sentry/Raven.pm
Criterion Covered Total %
statement 187 197 94.9
branch 28 34 82.3
condition 30 35 85.7
subroutine 52 56 92.8
pod 19 19 100.0
total 316 341 92.6


line stmt bran cond sub pod time code
1             package Sentry::Raven;
2              
3 8     8   465155 use 5.010;
  8         83  
4 8     8   49 use strict;
  8         14  
  8         168  
5 8     8   38 use warnings;
  8         14  
  8         594  
6 8     8   3853 use Moo;
  8         79768  
  8         42  
7 8     8   14756 use MooX::Types::MooseLike::Base qw/ ArrayRef HashRef Int Str /;
  8         53159  
  8         809  
8              
9             our $VERSION = '1.12';
10              
11 8     8   4173 use Data::Dump 'dump';
  8         49544  
  8         492  
12 8     8   4092 use Devel::StackTrace;
  8         26038  
  8         261  
13 8     8   2949 use English '-no_match_vars';
  8         17603  
  8         53  
14 8     8   2935 use File::Basename 'basename';
  8         18  
  8         492  
15 8     8   4204 use HTTP::Request::Common 'POST';
  8         147134  
  8         565  
16 8     8   2852 use HTTP::Status ':constants';
  8         27547  
  8         3101  
17 8     8   5406 use JSON::XS;
  8         34894  
  8         486  
18 8     8   5526 use LWP::UserAgent;
  8         157354  
  8         298  
19 8     8   3854 use Sys::Hostname;
  8         8386  
  8         464  
20 8     8   4293 use Time::Piece;
  8         59080  
  8         51  
21 8     8   656 use URI;
  8         26  
  8         304  
22 8     8   4116 use UUID::Tiny ':std';
  8         163168  
  8         1762  
23              
24             # constants from server-side sentry code
25             use constant {
26 8         1365 MAX_CULPRIT => 200,
27             MAX_MESSAGE => 2048,
28              
29             MAX_EXCEPTION_TYPE => 128,
30             MAX_EXCEPTION_VALUE => 4096,
31              
32             MAX_HTTP_QUERY_STRING => 1024,
33             MAX_HTTP_DATA => 2048,
34              
35             MAX_QUERY_ENGINE => 128,
36             MAX_QUERY_QUERY => 1024,
37              
38             MAX_STACKTRACE_FILENAME => 256,
39             MAX_STACKTRACE_PACKAGE => 256,
40             MAX_STACKTRACE_SUBROUTUNE => 256,
41              
42             MAX_USER_EMAIL => 128,
43             MAX_USER_ID => 128,
44             MAX_USER_USERNAME => 128,
45             MAX_USER_IP_ADDRESS => 45, # RFC 4291, section 2.2.3
46 8     8   65 };
  8         18  
47              
48             # self-imposed constants
49             use constant {
50 8         27354 MAX_HTTP_COOKIES => 1024,
51             MAX_HTTP_URL => 1024,
52              
53             MAX_STACKTRACE_VARS => 1024,
54 8     8   56 };
  8         17  
55              
56             =head1 NAME
57              
58             Sentry::Raven - A perl sentry client
59              
60             =head1 VERSION
61              
62             Version 1.12
63              
64             =head1 SYNOPSIS
65              
66             my $raven = Sentry::Raven->new( sentry_dsn => 'https://<publickey>:<secretkey>@sentry.io/<projectid>' );
67              
68             # capture all errors
69             $raven->capture_errors( sub {
70             ..do something here..
71             } );
72              
73             # capture an individual event
74             $raven->capture_message('The sky is falling');
75              
76             # annotate an event with context
77             $raven->capture_message(
78             'The sky is falling',
79             Sentry::Raven->exception_context('SkyException', 'falling'),
80             );
81              
82             =head1 DESCRIPTION
83              
84             This module implements the recommended raven interface for posting events to a sentry service.
85              
86             =head1 CONSTRUCTOR
87              
88             =head2 my $raven = Sentry::Raven->new( %options, %context )
89              
90             Create a new sentry interface object. It accepts the following named options:
91              
92             =over
93              
94             =item C<< sentry_dsn => 'http://<publickey>:<secretkey>@sentry.io/<projectid>' >>
95              
96             The DSN for your sentry service. Get this from the client configuration page for your project.
97              
98             =item C<< timeout => 5 >>
99              
100             Do not wait longer than this number of seconds when attempting to send an event.
101              
102             =back
103              
104             =cut
105              
106             has [qw/ post_url public_key secret_key /] => (
107             is => 'ro',
108             isa => Str,
109             required => 1,
110             );
111              
112             has sentry_version => (
113             is => 'ro',
114             isa => Int,
115             default => 7,
116             );
117              
118             has timeout => (
119             is => 'ro',
120             isa => Int,
121             default => 5,
122             );
123              
124             has json_obj => (
125             is => 'ro',
126             builder => '_build_json_obj',
127             lazy => 1,
128             );
129              
130             has ua_obj => (
131             is => 'ro',
132             builder => '_build_ua_obj',
133             lazy => 1,
134             );
135              
136             has valid_levels => (
137             is => 'ro',
138             isa => ArrayRef[Str],
139             default => sub { [qw/ fatal error warning info debug /] },
140             );
141              
142             has valid_interfaces => (
143             is => 'ro',
144             isa => ArrayRef[Str],
145             default => sub { [qw/
146             sentry.interfaces.Exception sentry.interfaces.Http
147             sentry.interfaces.Stacktrace sentry.interfaces.User
148             sentry.interfaces.Query
149             /] },
150             );
151              
152             has context => (
153             is => 'rw',
154             isa => HashRef[],
155             default => sub { { } },
156             );
157              
158             has processors => (
159             is => 'rw',
160             isa => ArrayRef[],
161             default => sub { [] },
162             );
163              
164             has encoding => (
165             is => 'rw',
166             isa => Str,
167             default => 'gzip',
168             );
169              
170             around BUILDARGS => sub {
171             my ($orig, $class, %args) = @_;
172              
173             my $sentry_dsn = $ENV{SENTRY_DSN} || $args{sentry_dsn}
174             or die "must pass sentry_dsn or set SENTRY_DSN envirionment variable\n";
175              
176             delete($args{sentry_dsn});
177              
178             my $uri = URI->new($sentry_dsn);
179              
180             die "unable to parse sentry dsn: $sentry_dsn\n"
181             unless defined($uri) && $uri->can('userinfo');
182              
183             die "unable to parse public and secret keys from: $sentry_dsn\n"
184             unless defined($uri->userinfo()) && $uri->userinfo() =~ m/:/;
185              
186             my @path = split(m{/}, $uri->path());
187             my ($public_key, $secret_key) = $uri->userinfo() =~ m/(.*):(.*)/;
188             my $project_id = pop(@path);
189              
190             my $post_url =
191             $uri->scheme().'://'.$uri->host().':'.$uri->port() .
192             join('/', @path).'/api/'.$project_id.'/store/'
193             ;
194              
195             my $timeout = delete($args{timeout});
196             my $ua_obj = delete($args{ua_obj});
197             my $processors = delete($args{processors}) || [];
198             my $encoding = delete($args{encoding});
199              
200             return $class->$orig(
201             post_url => $post_url,
202             public_key => $public_key,
203             secret_key => $secret_key,
204             context => \%args,
205             processors => $processors,
206              
207             (defined($encoding) ? (encoding => $encoding) : ()),
208             (defined($timeout) ? (timeout => $timeout) : ()),
209             (defined($ua_obj) ? (ua_obj => $ua_obj) : ()),
210             );
211             };
212              
213             sub _trim {
214 170     170   3054 my ($string, $length) = @_;
215 170 100       721 return defined($string)
216             ? substr($string, 0, $length)
217             : undef;
218             }
219              
220             =head1 ERROR HANDLERS
221              
222             These methods are designed to capture events and handle them automatically.
223              
224             =head2 $raven->capture_errors( $subref, %context )
225              
226             Execute the $subref and report any exceptions (die) back to the sentry service. If it is unable to submit an event (capture_message return undef), it will die and include the event details in the die message. This automatically includes a stacktrace unless C<$SIG{__DIE__}> has been overridden in subsequent code.
227              
228             =cut
229              
230             sub capture_errors {
231 2     2 1 279 my ($self, $subref, %context) = @_;
232              
233 2         4 my $wantarray = wantarray();
234              
235 2         4 my ($stacktrace, @retval);
236 2         4 eval {
237 2     2   12 local $SIG{__DIE__} = sub { $stacktrace = Devel::StackTrace->new(skip_frames => 1) };
  2         59  
238              
239 2 50       6 if ($wantarray) {
240 0         0 @retval = $subref->();
241             } else {
242 2         7 $retval[0] = $subref->();
243             }
244             };
245              
246 2         1019 my $eval_error = $EVAL_ERROR;
247              
248 2 50       6 if ($eval_error) {
249 2         4 my $message = $eval_error;
250 2         5 chomp($message);
251              
252 2 50       21 my %stacktrace_context = $stacktrace
253             ? $self->stacktrace_context(
254             $self->_get_frames_from_devel_stacktrace($stacktrace),
255             )
256             : ();
257              
258 2         21 %context = (
259             culprit => $PROGRAM_NAME,
260             %context,
261             $self->exception_context($message),
262             %stacktrace_context,
263             );
264              
265 2         10 my $event_id = $self->capture_message($message, %context);
266              
267 2 100       40 if (!defined($event_id)) {
268 1         7 die "failed to submit event to sentry service:\n" . dump($self->_construct_message_event($message, %context));
269             }
270             }
271              
272 1 50       26 return $wantarray ? @retval : $retval[0];
273             };
274              
275             sub _get_frames_from_devel_stacktrace {
276 3     3   2691 my ($self, $stacktrace) = @_;
277              
278             my @frames = map {
279 3         19 my $frame = $_;
  21         440  
280             {
281             abs_path => _trim($frame->filename(), MAX_STACKTRACE_FILENAME),
282             filename => _trim(basename($frame->filename()), MAX_STACKTRACE_FILENAME),
283             function => _trim($frame->subroutine(), MAX_STACKTRACE_SUBROUTUNE),
284             lineno => $frame->line(),
285             module => _trim($frame->package(), MAX_STACKTRACE_PACKAGE),
286             vars => {
287             '@_' => [
288 21         56 map { _trim(dump($_), MAX_STACKTRACE_VARS) } $frame->args(),
  16         79  
289             ],
290             },
291             }
292             } $stacktrace->frames();
293              
294             # Devel::Stacktrace::Frame's subroutine() and args() describe what's being called by the current frame,
295             # whereas Sentry expects function and vars to describe the current frame.
296 3         16 for my $i (0..$#frames) {
297 21         32 my $frame = $frames[$i];
298 21 100       41 my $parent = defined($frames[$i + 1])
299             ? $frames[$i + 1]
300             : {};
301 21         52 @$frame{'function', 'vars'} = @$parent{'function', 'vars'};
302             }
303              
304 3         14 return [ reverse(@frames) ];
305             }
306              
307             =head1 METHODS
308              
309             These methods are for generating individual events.
310              
311             =head2 $raven->capture_message( $message, %context )
312              
313             Post a string message to the sentry service. Returns the event id.
314              
315             =cut
316              
317             sub capture_message {
318 7     7 1 483 my ($self, $message, %context) = @_;
319 7         30 return $self->_post_event($self->_construct_message_event($message, %context));
320             }
321              
322             sub _construct_message_event {
323 9     9   1311 my ($self, $message, %context) = @_;
324 9         56 return $self->_construct_event(message => $message, %context);
325             }
326              
327             =head2 $raven->capture_exception( $exception_value, %exception_context, %context )
328              
329             Post an exception type and value to the sentry service. Returns the event id.
330              
331             C<%exception_context> can contain:
332              
333             =over
334              
335             =item C<< type => $type >>
336              
337             =back
338              
339             =cut
340              
341             sub capture_exception {
342 0     0 1 0 my ($self, $value, %context) = @_;
343 0         0 return $self->_post_event($self->_construct_exception_event($value, %context));
344             };
345              
346             sub _construct_exception_event {
347 1     1   3528 my ($self, $value, %context) = @_;
348 1         7 return $self->_construct_event(
349             %context,
350             $self->exception_context($value, %context),
351             );
352             };
353              
354             =head2 $raven->capture_request( $url, %request_context, %context )
355              
356             Post a web url request to the sentry service. Returns the event id.
357              
358             C<%request_context> can contain:
359              
360             =over
361              
362             =item C<< method => 'GET' >>
363              
364             =item C<< data => 'foo=bar' >>
365              
366             =item C<< query_string => 'foo=bar' >>
367              
368             =item C<< cookies => 'foo=bar' >>
369              
370             =item C<< headers => { 'Content-Type' => 'text/html' } >>
371              
372             =item C<< env => { REMOTE_ADDR => '192.168.0.1' } >>
373              
374             =back
375              
376             =cut
377              
378             sub capture_request {
379 0     0 1 0 my ($self, $url, %context) = @_;
380 0         0 return $self->_post_event($self->_construct_request_event($url, %context));
381             };
382              
383             sub _construct_request_event {
384 1     1   3212 my ($self, $url, %context) = @_;
385              
386 1         7 return $self->_construct_event(
387             %context,
388             $self->request_context($url, %context),
389             );
390             };
391              
392             =head2 $raven->capture_stacktrace( $frames, %context )
393              
394             Post a stacktrace to the sentry service. Returns the event id.
395              
396             C<$frames> can be either a Devel::StackTrace object, or an arrayref of hashrefs with each hashref representing a single frame.
397              
398             my $frames = [
399             {
400             filename => 'my/file1.pl',
401             function => 'function1',
402             vars => { foo => 'bar' },
403             lineno => 10,
404             },
405             {
406             filename => 'my/file2.pl',
407             function => 'function2',
408             vars => { bar => 'baz' },
409             lineno => 20,
410             },
411             ];
412              
413             The first frame should be the oldest frame. Frames must contain at least one of C<filename>, C<function>, or C<module>. These additional attributes are also supported:
414              
415             =over
416              
417             =item C<< filename => $file_name >>
418              
419             =item C<< function => $function_name >>
420              
421             =item C<< module => $module_name >>
422              
423             =item C<< lineno => $line_number >>
424              
425             =item C<< colno => $column_number >>
426              
427             =item C<< abs_path => $absolute_path_file_name >>
428              
429             =item C<< context_line => $line_of_code >>
430              
431             =item C<< pre_context => [ $previous_line1, $previous_line2 ] >>
432              
433             =item C<< post_context => [ $next_line1, $next_line2 ] >>
434              
435             =item C<< in_app => $one_if_not_external_library >>
436              
437             =item C<< vars => { $variable_name => $variable_value } >>
438              
439             =back
440              
441             =cut
442              
443             sub capture_stacktrace {
444 1     1 1 24 my ($self, $frames, %context) = @_;
445 1         6 return $self->_post_event($self->_construct_stacktrace_event($frames, %context));
446             };
447              
448             sub _construct_stacktrace_event {
449 3     3   4844 my ($self, $frames, %context) = @_;
450              
451 3         14 return $self->_construct_event(
452             %context,
453             $self->stacktrace_context($frames),
454             );
455             };
456              
457             =head2 $raven->capture_user( %user_context, %context )
458              
459             Post a user to the sentry service. Returns the event id.
460              
461             C<%user_context> can contain:
462              
463             =over
464              
465             =item C<< id => $unique_id >>
466              
467             =item C<< username => $username >>
468              
469             =item C<< email => $email >>
470              
471             =item C<< ip_address => $ip_address >>
472              
473             =back
474              
475             =cut
476              
477             sub capture_user {
478 0     0 1 0 my ($self, %context) = @_;
479 0         0 return $self->_post_event($self->_construct_user_event(%context));
480             };
481              
482             sub _construct_user_event {
483 1     1   3438 my ($self, %context) = @_;
484              
485             return $self->_construct_event(
486             %context,
487             $self->user_context(
488 1         6 map { $_ => $context{$_} } qw/email id username ip_address/
  4         24  
489             ),
490             );
491             };
492              
493             =head2 $raven->capture_query( $query, %query_context, %context )
494              
495             Post a query to the sentry service. Returns the event id.
496              
497             C<%query_context> can contain:
498              
499             =over
500              
501             =item C<< engine => $engine' >>
502              
503             =back
504              
505             =cut
506              
507             sub capture_query {
508 0     0 1 0 my ($self, $query, %context) = @_;
509 0         0 return $self->_post_event($self->_construct_query_event($query, %context));
510             };
511              
512             sub _construct_query_event {
513 1     1   2469 my ($self, $query, %context) = @_;
514              
515 1         5 return $self->_construct_event(
516             %context,
517             $self->query_context($query, %context),
518             );
519             };
520              
521             sub _post_event {
522 9     9   116 my ($self, $event) = @_;
523              
524 9         30 $event = $self->_process_event($event);
525              
526 9         19 my ($response, $response_code, $response_content);
527              
528 9         19 eval {
529 9         159 my $event_json = $self->json_obj()->encode( $event );
530              
531 8         195 $self->ua_obj()->timeout($self->timeout());
532              
533 8         251 my $request = POST(
534             $self->post_url(),
535             'X-Sentry-Auth' => $self->_generate_auth_header(),
536             Content => $event_json,
537             );
538 8         3641 $request->encode( $self->encoding() );
539 8         66357 $response = $self->ua_obj()->request($request);
540              
541 8         8670 $response_code = $response->code();
542 8         83 $response_content = $response->content();
543             };
544              
545 9 100       159 warn "$EVAL_ERROR\n" if $EVAL_ERROR;
546              
547 9 100 100     89 if (defined($response_code) && $response_code == HTTP_OK) {
548 6         160 return $self->json_obj()->decode($response_content)->{id};
549             } else {
550 3 100       12 if ($response) {
551 2         10 warn "Unsuccessful Response Posting Sentry Event:\n"._trim($response->as_string(), 1000)."\n";
552             }
553 3         91 return;
554             }
555             }
556              
557             sub _process_event {
558 9     9   23 my ($self, $event) = @_;
559              
560 9         14 foreach my $processor (@{$self->processors()}) {
  9         193  
561 2         26 my $processed_event = $processor->process($event);
562 2 50       15 if ($processed_event) {
563 2         5 $event = $processed_event;
564             } else {
565 0         0 die "processor $processor did not return an event";
566             }
567             }
568              
569 9         65 return $event;
570             }
571              
572             sub _generate_id {
573 20     20   295 (my $uuid = create_uuid_as_string(UUID_V4)) =~ s/-//g;
574 20         3985 return $uuid;
575             }
576              
577             sub _construct_event {
578 23     23   11795 my ($self, %context) = @_;
579              
580             my $event = {
581             event_id => $context{event_id} || $self->context()->{event_id} || _generate_id(),
582             timestamp => $context{timestamp} || $self->context()->{timestamp} || gmtime->datetime(),
583             logger => $context{logger} || $self->context()->{logger} || 'root',
584             server_name => $context{server_name} || $self->context()->{server_name} || hostname(),
585             platform => $context{platform} || $self->context()->{platform} || 'perl',
586              
587             release => $context{release} || $self->context()->{release},
588              
589             message => $context{message} || $self->context()->{message},
590             culprit => $context{culprit} || $self->context()->{culprit},
591              
592             extra => $self->_merge_hashrefs($self->context()->{extra}, $context{extra}),
593             tags => $self->_merge_hashrefs($self->context()->{tags}, $context{tags}),
594             fingerprint => $context{fingerprint} || $self->context()->{fingerprint} || ['{{ default }}'],
595              
596             level => $self->_validate_level($context{level}) || $self->context()->{level} || 'error',
597             environment => $context{environment} || $self->context()->{environment} || $ENV{SENTRY_ENVIRONMENT},
598 23   66     593 };
      66        
      100        
      66        
      100        
      66        
      100        
      100        
      100        
      100        
      66        
599              
600 23         778 $event->{message} = _trim($event->{message}, MAX_MESSAGE);
601 23         63 $event->{culprit} = _trim($event->{culprit}, MAX_CULPRIT);
602              
603 23         477 my $instance_ctx = $self->context();
604 23         136 foreach my $interface (@{ $self->valid_interfaces() }) {
  23         93  
605 115   100     310 my $interface_ctx = $context{$interface} || $instance_ctx->{$interface};
606 115 100       234 $event->{$interface} = $interface_ctx
607             if $interface_ctx;
608             }
609              
610 23         118 return $event;
611             }
612              
613             sub _merge_hashrefs {
614 48     48   6065 my ($self, $hash1, $hash2) = @_;
615              
616             return {
617 8         91 ($hash1 ? %{ $hash1 } : ()),
618 48 100       732 ($hash2 ? %{ $hash2 } : ()),
  6 100       85  
619             };
620             };
621              
622             sub _validate_level {
623 23     23   258 my ($self, $level) = @_;
624              
625 23 100       291 return unless defined($level);
626              
627 10         16 my %level_hash = map { $_ => 1 } @{ $self->valid_levels() };
  50         108  
  10         42  
628              
629 10 100       31 if (exists($level_hash{$level})) {
630 9         191 return $level;
631             } else {
632 1         14 warn "unknown level: $level\n";
633 1         25 return;
634             }
635             };
636              
637             sub _generate_auth_header {
638 8     8   19 my ($self) = @_;
639              
640 8         92 my %fields = (
641             sentry_version => $self->sentry_version(),
642             sentry_client => "raven-perl/$VERSION",
643             sentry_timestamp => time(),
644              
645             sentry_key => $self->public_key(),
646             sentry_secret => $self->secret_key(),
647             );
648              
649 8         59 return 'Sentry ' . join(', ', map { $_ . '=' . $fields{$_} } sort keys %fields);
  40         165  
650             }
651              
652 7     7   316 sub _build_json_obj { JSON::XS->new()->utf8(1)->pretty(1)->allow_nonref(1) }
653             sub _build_ua_obj {
654 1     1   106 return LWP::UserAgent->new(
655             keep_alive => 1,
656             );
657             }
658              
659             =head1 EVENT CONTEXT
660              
661             These methods are for annotating events with additional context, such as stack traces or HTTP requests. Simply pass their output to any other method accepting C<%context>. They accept all of the same arguments as their C<capture_*> counterparts.
662              
663             $raven->capture_message(
664             'The sky is falling',
665             Sentry::Raven->exception_context('falling', type => 'SkyException'),
666             );
667              
668             =head2 Sentry::Raven->exception_context( $value, %exception_context )
669              
670             =cut
671              
672             sub exception_context {
673 4     4 1 3178 my ($class, $value, %exception_context) = @_;
674              
675             return (
676             'sentry.interfaces.Exception' => {
677             value => _trim($value, MAX_EXCEPTION_VALUE),
678 4         12 type => _trim($exception_context{type}, MAX_EXCEPTION_TYPE),
679             }
680             );
681             };
682              
683             =head2 Sentry::Raven->request_context( $url, %request_context )
684              
685             =cut
686              
687             sub request_context {
688 1     1 1 4 my ($class, $url, %context) = @_;
689              
690             return (
691             'sentry.interfaces.Http' => {
692             url => _trim($url, MAX_HTTP_URL),
693             method => $context{method},
694             data => _trim($context{data}, MAX_HTTP_DATA),
695             query_string => _trim($context{query_string}, MAX_HTTP_QUERY_STRING),
696             cookies => _trim($context{cookies}, MAX_HTTP_COOKIES),
697             headers => $context{headers},
698             env => $context{env},
699             }
700 1         4 );
701             };
702              
703             =head2 Sentry::Raven->stacktrace_context( $frames )
704              
705             =cut
706              
707             sub stacktrace_context {
708 5     5 1 14 my ($class, $frames) = @_;
709              
710 5         8 eval {
711 5 50       43 $frames = $class->_get_frames_from_devel_stacktrace($frames)
712             if $frames->isa('Devel::StackTrace');
713             };
714              
715             return (
716 5         24 'sentry.interfaces.Stacktrace' => {
717             frames => $frames,
718             }
719             );
720             };
721              
722             =head2 Sentry::Raven->user_context( %user_context )
723              
724             =cut
725              
726             sub user_context {
727 2     2 1 3162 my ($class, %user_context) = @_;
728 2         10 my ($email, $id, $username, $ip_address) = delete @user_context{qw/email id username ip_address/};
729              
730             return (
731 2         6 'sentry.interfaces.User' => {
732             email => _trim($email, MAX_USER_EMAIL),
733             id => _trim($id, MAX_USER_ID),
734             username => _trim($username, MAX_USER_USERNAME),
735             ip_address => _trim($ip_address, MAX_USER_IP_ADDRESS),
736             %user_context,
737             }
738             );
739             };
740              
741             =head2 Sentry::Raven->query_context( $query, %query_context )
742              
743             =cut
744              
745             sub query_context {
746 1     1 1 4 my ($class, $query, %query_context) = @_;
747              
748             return (
749             'sentry.interfaces.Query' => {
750             query => _trim($query, MAX_QUERY_QUERY),
751 1         4 engine => _trim($query_context{engine}, MAX_QUERY_ENGINE),
752             }
753             );
754             };
755              
756             =pod
757              
758             The default context can be modified with the following accessors:
759              
760             =head2 my %context = $raven->get_context();
761              
762             =cut
763              
764             sub get_context {
765 2     2 1 1272 my ($self) = @_;
766 2         5 return %{ $self->context() };
  2         41  
767             };
768              
769             =head2 $raven->add_context( %context )
770              
771             =cut
772              
773             sub add_context {
774 3     3 1 9079 my ($self, %context) = @_;
775             $self->context()->{$_} = $context{$_}
776 3         84 for keys %context;
777             };
778              
779             =head2 $raven->merge_tags( %tags )
780              
781             Merge additional tags into any existing tags in the current context.
782              
783             =cut
784              
785             sub merge_tags {
786 1     1 1 15 my ($self, %tags) = @_;
787 1         19 $self->context()->{tags} = $self->_merge_hashrefs($self->context()->{tags}, \%tags);
788             };
789              
790             =head2 $raven->merge_extra( %tags )
791              
792             Merge additional extra into any existing extra in the current context.
793              
794             =cut
795              
796             sub merge_extra {
797 1     1 1 15 my ($self, %extra) = @_;
798 1         20 $self->context()->{extra} = $self->_merge_hashrefs($self->context()->{extra}, \%extra);
799             };
800              
801             =head2 $raven->clear_context()
802              
803             =cut
804              
805             sub clear_context {
806 1     1 1 1164 my ($self) = @_;
807 1         25 $self->context({});
808             };
809              
810             =head1 EVENT PROCESSORS
811              
812             Processors are a mechanism for modifying events after they are generated but before they are posted to the sentry service. They are useful for scrubbing sensitive data, such as passwords, as well as adding additional context. If the processor fails (dies or returns undef), the failure will be passed to the caller.
813              
814             See L<Sentry::Raven::Processor> for information on creating new processors.
815              
816             Available processors:
817              
818             =over
819              
820             =item L<Sentry::Raven::Processor::RemoveStackVariables>
821              
822             =back
823              
824             =head2 $raven->add_processors( [ Sentry::Raven::Processor::RemoveStackVariables, ... ] )
825              
826             =cut
827              
828             sub add_processors {
829 3     3 1 1420 my ($self, @processors) = @_;
830 3         4 push @{ $self->processors() }, @processors;
  3         64  
831             };
832              
833             =head2 $raven->clear_processors( [ Sentry::Raven::Processor::RemoveStackVariables, ... ] )
834              
835             =cut
836              
837             sub clear_processors {
838 3     3 1 6302 my ($self) = @_;
839 3         80 $self->processors([]);
840             };
841              
842             =head1 STANDARD OPTIONS
843              
844             These options can be passed to all methods accepting %context. Passing context to the constructor overrides defaults.
845              
846             =over
847              
848             =item C<< culprit => 'Some::Software' >>
849              
850             The source of the event. Defaults to C<undef>.
851              
852             =item C<< event_id => '534188f7c1ff4ff280c2e1206c9e0548' >>
853              
854             The unique identifier string for an event, usually UUID v4. Max 32 characters. Defaults to a new unique UUID for each event. Invalid ids may be discarded silently.
855              
856             =item C<< extra => { key1 => 'val1', ... } >>
857              
858             Arbitrary key value pairs with extra information about an event. Defaults to C<{}>.
859              
860             =item C<< level => 'error' >>
861              
862             Event level of an event. Acceptable values are C<fatal>, C<error>, C<warning>, C<info>, and C<debug>. Defaults to C<error>.
863              
864             =item C<< logger => 'root' >>
865              
866             The creator of an event. Defaults to 'root'.
867              
868             =item C<< platform => 'perl' >>
869              
870             The platform (language) in which an event occurred. Defaults to C<perl>.
871              
872             =item C<< release => 'ec899ea' >>
873              
874             Track the release version of your application.
875              
876             =item C<< processors => [ Sentry::Raven::Processor::RemoveStackVariables, ... ] >>
877              
878             A set or processors to be applied to events before they are posted. See L<Sentry::Raven::Processor> for more information. This can only be set during construction and not on other methods accepting %context.
879              
880             =item C<< server_name => 'localhost.example.com' >>
881              
882             The hostname on which an event occurred. Defaults to the system hostname.
883              
884             =item C<< tags => { key1 => 'val1, ... } >>
885              
886             Arbitrary key value pairs with tags for categorizing an event. Defaults to C<{}>.
887              
888             =item C<< fingerprint => [ 'val1', 'val2', ... } >>
889              
890             Array of strings used to control how events aggregate in the sentry web interface. The string C<'{{ default }}'> has special meaning when used as the first value; it indicates that sentry should use the default aggregation method in addition to any others specified (useful for fine-grained aggregation). Defaults to C<['{{ default }}']>.
891              
892             =item C<< timestamp => '1970-01-01T00:00:00' >>
893              
894             Timestamp of an event. ISO 8601 format. Defaults to the current time. Invalid values may be discarded silently.
895              
896             =item C<< environment => 'production' >>
897              
898             Specify the environment (i.e. I<staging>, I<production>, etc.) that your project is deployed in. More information
899             can be found on the L<Sentry website|https://docs.sentry.io/enriching-error-data/environments/>.
900              
901             =back
902              
903             =head1 CONFIGURATION AND ENVIRONMENT
904              
905             =over
906              
907             =item SENTRY_DSN=C<< http://<publickey>:<secretkey>@sentry.io/<projectid> >>
908              
909             A default DSN to be used if sentry_dsn is not passed to c<new>.
910              
911             =back
912              
913             =head1 LICENSE
914              
915             Copyright (C) 2019 by Matt Harrington
916              
917             The full text of this license can be found in the LICENSE file included with this module.
918              
919             =cut
920              
921             1;