File Coverage

/root/.cpan/build/Sentry-0.01-0/blib/lib/Sentry.pm
Criterion Covered Total %
statement 45 58 77.5
branch 4 14 28.5
condition 9 24 37.5
subroutine 12 19 63.1
pod 1 7 14.2
total 71 122 58.2


line stmt bran cond sub pod time code
1             # ABSTRACT: Yet another lightweight Sentry client
2              
3             package Sentry;
4             $Sentry::VERSION = '0.01';
5              
6              
7 1     1   817 use LWP::UserAgent;
  1         39382  
  1         25  
8 1     1   359 use MIME::Base64 'encode_base64';
  1         469  
  1         46  
9 1     1   350 use Sys::Hostname;
  1         767  
  1         38  
10 1     1   383 use POSIX;
  1         4840  
  1         4  
11 1     1   2602 use JSON::XS;
  1         3909  
  1         42  
12 1     1   329 use Sub::Name;
  1         370  
  1         38  
13 1     1   5 use Carp;
  1         2  
  1         34  
14 1     1   426 use Class::Tiny;
  1         2239  
  1         4  
15              
16             my @LEVELS;
17              
18             BEGIN {
19 1     1   3 @LEVELS = qw( fatal error warning warn info debug );
20 1     1   79 no strict 'refs';
  1         1  
  1         64  
21 1         2 for my $level (@LEVELS) {
22 6         495 *{ __PACKAGE__ . "::$level" } = subname $level =>
23 6     0 0 27 sub { shift->_send( message => shift, level => $level, @_ ) };
  0     0 0 0  
        0 0    
        0 0    
        0 0    
        0 0    
24             }
25             }
26              
27             my @INTERFACES = (
28             'exception', 'stacktrace', 'template', 'breadcrumbs',
29             'contexts', 'request', 'threads', 'user',
30             'debug_meta', 'repos', 'sdk'
31             );
32              
33              
34             sub new {
35 1     1 1 66 my ( $class, $dsn, %params ) = @_;
36              
37 1 50       10 die 'API key is not defined' unless $dsn;
38              
39             my $self = {
40             ua => LWP::UserAgent->new( timeout => 10 ),
41 1   50     8 sentry_version => $params{sentry_version} || 7,
42             %params,
43             };
44              
45             (
46             my $protocol, $self->{public_key}, $self->{secret_key},
47 1         2217 my $host_path,
48             my $project_id
49             )
50             = $dsn =~ m{^ ( https? ) :// ( \w+ ) : ( \w+ ) @ ( .+ ) / ( \d+ ) $}ixaa;
51              
52             die 'Wrong dsn format'
53 5   33     16 if grep { !defined $_ || !length $_ } (
54 1 50       4 $protocol, $self->{public_key}, $self->{secret_key}, $host_path,
55             $project_id
56             );
57              
58 1         4 $self->{uri} = "$protocol://$host_path/api/$project_id/store/";
59              
60 1         3 bless $self, $class;
61             }
62              
63             # Send a message to Sentry server.
64             # Returns the id of inserted message or dies.
65              
66             sub _send {
67 0     0   0 my ( $self, %params ) = @_;
68              
69             my $auth = sprintf
70             'Sentry sentry_version=%s, sentry_timestamp=%s, sentry_key=%s, sentry_client=%s, sentry_secret=%s',
71             $self->{sentry_version},
72             time(),
73             $self->{public_key},
74             __PACKAGE__,
75             $self->{secret_key},
76 0         0 ;
77              
78 0         0 my $message = $self->_build_message(%params);
79 0         0 $message = encode_json $message;
80             my $response = $self->{ua}->post(
81             $self->{uri},
82 0         0 'X-Sentry-Auth' => $auth,
83             'Content-Type' => 'application/json',
84             Content => encode_base64($message),
85             );
86              
87 0 0       0 unless ( $response->is_success ) {
88 0 0       0 if ( int( $response->code / 100 ) == 4 ) {
89 0         0 die $response->status_line . ': ' . $response->decoded_content;
90             }
91              
92 0         0 die $response->status_line;
93             }
94              
95 0         0 my $answer_ref = decode_json $response->decoded_content;
96              
97 0 0 0     0 die 'Wrong answer format' unless $answer_ref && $answer_ref->{id};
98              
99 0         0 return $answer_ref->{id};
100             }
101              
102             sub _build_message {
103 2     2   3353 my ( $self, %params ) = @_;
104              
105             die 'No message given'
106 2 50 33     12 unless defined $params{message} && length $params{message};
107              
108             my $data_ref = {
109             message => $params{message},
110             timestamp => strftime( '%FT%X.000000Z', gmtime time ),
111             level => $params{level} || $self->{level} || 'info',
112             logger => $params{logger},
113             platform => $params{platform} || 'perl',
114             culprit => $params{culprit} || '',
115             tags => { %{ $self->{tags} }, %{ $params{tags} } } || {},
116             server_name => $params{server_name} || hostname(),
117             modules => $params{modules},
118             extra => $params{extra} || {},
119 2   50     104 };
      50        
      50        
      50        
      33        
      50        
120              
121 2         35 for (@INTERFACES) {
122 22 50       31 $data_ref->{$_} => $params{$_} if $params{$_};
123             }
124              
125 2         6 return $data_ref;
126             }
127              
128             1;
129              
130             __END__