File Coverage

blib/lib/Net/Airbrake.pm
Criterion Covered Total %
statement 60 62 96.7
branch 5 8 62.5
condition 4 8 50.0
subroutine 15 15 100.0
pod 4 4 100.0
total 88 97 90.7


line stmt bran cond sub pod time code
1             package Net::Airbrake;
2              
3 2     2   797 use strict;
  2         2  
  2         52  
4 2     2   7 use warnings;
  2         2  
  2         70  
5              
6             our $VERSION = '0.01';
7              
8 2     2   2004 use HTTP::Tiny;
  2         76977  
  2         79  
9 2     2   18 use JSON qw(decode_json);
  2         2  
  2         27  
10 2     2   1378 use Scope::Guard qw(guard);
  2         799  
  2         98  
11 2     2   714 use Net::Airbrake::Request;
  2         5  
  2         51  
12 2     2   727 use Net::Airbrake::Error;
  2         4  
  2         151  
13              
14             use Class::Tiny {
15             api_key => undef,
16             project_id => undef,
17             environment => $ENV{AIRBRAKE_ENV} || $ENV{PLACK_ENV} || 'default',
18 1         25 _errors => sub { [] },
19 1         13 _ua => sub { HTTP::Tiny->new(agent => "Net-Airbrake/$VERSION", timeout => 5) }
20 2   50 2   9 };
  2         2  
  2         48  
21              
22             sub add_error {
23 2     2 1 4 my $self = shift;
24 2         2 my ($error) = @_;
25              
26 2         2 push @{$self->_errors}, Net::Airbrake::Error->new($error);
  2         52  
27             }
28              
29             sub has_error {
30 2 50   2 1 3 scalar @{shift->_errors} ? 1 : 0;
  2         45  
31             }
32              
33             sub send {
34 2     2 1 2 my $self = shift;
35 2         3 my ($option) = @_;
36              
37 2 50       6 return unless $self->has_error;
38              
39 2         22 $self->_conceal_options($option);
40              
41 2 50       34 my $context = {
42             os => $^O,
43             language => "Perl $^V",
44             environment => $self->environment,
45 2         82 %{$option->{context} || {}},
46             };
47 2   50     41 my $req = Net::Airbrake::Request->new({
      50        
      50        
48             errors => $self->_errors,
49             context => $context,
50             environment => $option->{environment} || {},
51             session => $option->{session} || {},
52             params => $option->{params} || {},
53             });
54              
55 2     2   169 my $guard = guard { $self->_errors([]) };
  2         64  
56 2         69 my $res = $self->_ua->request(POST => $self->_url, {
57             content => $req->to_json,
58             headers => { 'Content-Type' => 'application/json' },
59             });
60 2 100       236 die "Request failed to Airbrake: @{[$res->{status}]} @{[$res->{reason}]} (@{[$res->{content}]})"
  1         4  
  1         7  
  1         9  
61             unless $res->{success};
62              
63 1         13 decode_json($res->{content});
64             }
65              
66             sub notify {
67 2     2 1 3735 my $self = shift;
68 2         5 my ($error, $option) = @_;
69              
70 2         4 $self->add_error($error);
71 2         48 $self->send($option);
72             }
73              
74             sub _url {
75 2     2   110 my $self = shift;
76              
77 2         3 "https://airbrake.io/api/v3/projects/@{[$self->project_id]}/notices?key=@{[$self->api_key]}";
  2         34  
  2         44  
78             }
79              
80             sub _conceal_options {
81 2     2   3 my $self = shift;
82 2         2 my ($option) = @_;
83              
84 2         3 for my $opt (qw(environment session params)) {
85 6         5 for my $key (grep { /(?:cookie|password)/i } keys %{$option->{$opt}}) {
  0         0  
  6         23  
86 0           $option->{$opt}{$key} =~ s/./*/g;
87             }
88             }
89             }
90              
91             1;
92             __END__