File Coverage

blib/lib/SlapbirdAPM/Agent/CGI.pm
Criterion Covered Total %
statement 50 85 58.8
branch 2 16 12.5
condition 0 9 0.0
subroutine 16 17 94.1
pod n/a
total 68 127 53.5


line stmt bran cond sub pod time code
1             package SlapbirdAPM::Agent::CGI;
2              
3 1     1   118283 use strict;
  1         2  
  1         49  
4 1     1   6 use warnings;
  1         9  
  1         69  
5              
6 1     1   9 use Carp;
  1         3  
  1         78  
7 1     1   1787 use CGI;
  1         59824  
  1         9  
8 1     1   1023 use LWP::UserAgent;
  1         42851  
  1         45  
9 1     1   599 use POSIX ();
  1         10040  
  1         54  
10 1     1   1213 use SlapbirdAPM::CGI::DBIx::Tracer;
  1         7  
  1         59  
11 1     1   1110 use IO::Tee;
  1         17736  
  1         83  
12 1     1   2127 use IO::Pipe;
  1         1685  
  1         58  
13 1     1   10 use Time::HiRes;
  1         2  
  1         13  
14 1     1   70 use HTTP::Request;
  1         2  
  1         39  
15 1     1   7 use HTTP::Response;
  1         3  
  1         28  
16 1     1   907 use System::Info;
  1         29797  
  1         93  
17 1     1   1012 use JSON;
  1         14096  
  1         9  
18              
19             $Carp::Internal{__PACKAGE__} = 1;
20              
21             our $VERSION = '0.05';
22              
23             my %request_headers;
24             our $cgi = CGI->new();
25             our $handler;
26             our $start_time;
27             our @error;
28             our $queries = [];
29             our $key;
30             pipe( our $reader, our $writer );
31              
32             SlapbirdAPM::CGI::DBIx::Tracer->new(
33             sub {
34             my %args = @_;
35             push @$queries, { sql => $args{sql}, total_time => $args{time} };
36             }
37             );
38              
39             sub import {
40 1     1   20 $key = $ENV{SLAPBIRDAPM_API_KEY};
41              
42 1 50       3 if ( !$key ) {
43 1         48 warn(
44             "Your SlapbirdAPM key is not set, set the SLAPBIRDAPM_API_KEY environment variable to use SlapbirdAPM."
45             );
46 1         29 return;
47             }
48              
49 0           $start_time = time * 1_000;
50 0           ($handler) = caller;
51 0           %request_headers = map { $_ => $cgi->http($_) } $cgi->http();
  0            
52 0           local *tee = IO::Tee->new( $writer, *STDOUT{IO} );
53              
54 0           *{OLD_STDOUT} = *STDOUT{IO};
55 0           *{STDOUT} = *tee;
56              
57             $SIG{__DIE__} = sub {
58 0     0     @error = @_;
59 0           };
60              
61 0           return;
62             }
63              
64             END {
65 1 50   1   238518 if ( !$key ) {
66 1         23 close($writer);
67 1         23 close($reader);
68 1         49 return;
69             }
70              
71 0         0 my $end_time = time * 1_000;
72              
73 0         0 close($writer);
74 0         0 my $raw_response = do {
75 0         0 local $/ = undef;
76 0         0 <$reader>;
77             };
78 0         0 close($reader);
79              
80 0 0       0 if ( $raw_response !~ /^HTTP\/\d+\s\d+\s[A-Za-z].*/mxi ) {
81 0 0       0 if ( !@error ) {
82 0         0 $raw_response = "HTTP/1.1 200 OK\r\n$raw_response";
83             }
84             else {
85 0         0 $raw_response =
86             "HTTP/1.1 500 Internal Server Error\r\n$raw_response";
87             }
88             }
89              
90 0         0 local $SIG{CHLD} = 'IGNORE';
91              
92 0 0       0 if ( fork() ) {
93 0         0 return;
94             }
95              
96 0         0 my $res = HTTP::Response->parse($raw_response);
97              
98             my $slapbird_hash = {
99             type => 'cgi',
100             method => $ENV{REQUEST_METHOD},
101             end_point => $cgi->url( -path_info => 1, -query => 1, -absolute => 1 ),
102             start_time => $start_time,
103             end_time => $end_time,
104             response_code => +$res->code,
105             response_size => $res->header('content-length')
106             // length( $res->content ) // 0,
107             response_headers => +{ $res->headers->flatten() },
108             request_headers => \%request_headers,
109             request_size => $request_headers{HTTP_CONTENT_LENGTH} // 0,
110             error => join( "\n", @error ),
111 0 0 0     0 requestor => $request_headers{HTTP_X_SLAPBIRD_NAME} // 'UNKNOWN',
      0        
      0        
      0        
112             handler => $handler eq 'main' ? 'CGI' : $handler,
113             stack => [], # We don't trace stacks in CGI, because the overhead
114             os => System::Info->new->os,
115             queries => $queries,
116             num_queries => scalar @$queries
117             };
118              
119 0         0 my $ua = LWP::UserAgent->new();
120              
121             my $uri =
122             $ENV{SLAPBIRD_APM_URI}
123 0 0       0 ? $ENV{SLAPBIRD_APM_URI} . '/apm'
124             : 'https://slapbirdapm.com/apm';
125              
126 0         0 my $sb_request = HTTP::Request->new( POST => $uri );
127 0         0 $sb_request->content_type('application/json');
128 0         0 $sb_request->content( encode_json($slapbird_hash) );
129 0         0 $sb_request->header( 'x-slapbird-apm' => $key );
130 0         0 my $result = $ua->request($sb_request);
131              
132 0 0       0 if ( !$result->is_success ) {
133 0         0 warn( "Unable to communicate with Slapbird, got status code: "
134             . $result->code );
135             }
136              
137 0         0 POSIX::_exit(0);
138             }
139              
140             1;
141              
142             =pod
143              
144             =encoding utf8
145              
146             =head1 NAME
147              
148             SlapbirdAPM::Agent::CGI
149              
150             The L user-agent for L applications.
151              
152             =head1 SYNOPSIS
153              
154             =over 2
155              
156             =item *
157              
158             Create an application on L
159              
160             =item *
161              
162             Install this ie C, C
163              
164             =item *
165              
166             Add C near the top of your L script
167              
168             =item *
169              
170             Add your API key to your environment, in Apache that looks like: C
171              
172             =item *
173              
174             Restart your web-server
175              
176             =back
177              
178             =head1 EXAMPLE
179              
180             #!/usr/bin/env perl
181            
182             use strict;
183             use warnings;
184            
185             use DBI;
186             use SlapbirdAPM::Agent::CGI;
187             use CGI;
188            
189             my $dbh = DBI->connect('dbi:SQLite:dbname=file.db', '', '');
190            
191             my $sth = $dbh->prepare(q[select time('now');]);
192             $sth->execute();
193             my $time = $sth->fetch->[0];
194             my $response = 'Hello World! It is ' . $time . " o'clock";
195            
196             my $cgi = CGI->new();
197            
198             print $cgi->header();
199             print <<"END"
200            
201            
202            
203            

$response

204            
205            
206             END
207              
208             =head1 SEE ALSO
209              
210             L
211              
212             L
213              
214             L
215              
216             =head1 AUTHOR
217              
218             Mollusc Software Solutions (formerly Mollusc Labs), C
219              
220             =head1 LICENSE
221              
222             SlapbirdAPM::Agent::CGI like all SlapbirdAPM user-agents is licensed under the MIT license.
223              
224             SlapbirdAPM (the website) however, is licensed under the GNU AGPL version 3.0.
225              
226             =cut