File Coverage

blib/lib/Plack/Middleware/Statsd.pm
Criterion Covered Total %
statement 116 120 96.6
branch 33 50 66.0
condition 5 10 50.0
subroutine 20 20 100.0
pod 2 2 100.0
total 176 202 87.1


line stmt bran cond sub pod time code
1             package Plack::Middleware::Statsd;
2              
3             # ABSTRACT: send statistics to statsd
4              
5             # RECOMMEND PREREQ: Net::Statsd::Tiny v0.3.0
6             # RECOMMEND PREREQ: HTTP::Status 6.16
7             # RECOMMEND PREREQ: List::Util::XS
8             # RECOMMEND PREREQ: Ref::Util::XS
9              
10 5     5   1801011 use v5.20;
  5         21  
11 5     5   31 use warnings;
  5         10  
  5         373  
12              
13 5     5   28 use parent qw/ Plack::Middleware /;
  5         8  
  5         43  
14              
15 5     5   30306 use List::Util qw/ first /;
  5         14  
  5         367  
16 5     5   56 use Plack::Util;
  5         9  
  5         177  
17             use Plack::Util::Accessor
18 5     5   28 qw/ client sample_rate histogram increment set_add catch_errors /;
  5         9  
  5         35  
19 5     5   3071 use Ref::Util qw/ is_coderef /;
  5         13334  
  5         494  
20 5     5   39 use Time::HiRes;
  5         10  
  5         43  
21 5     5   2855 use Try::Tiny;
  5         8887  
  5         375  
22              
23 5     5   2478 use experimental qw/ postderef signatures /;
  5         13179  
  5         31  
24              
25             our $VERSION = 'v0.8.2';
26              
27             # Note: You may be able to omit the client if there is a client
28             # defined in the environment hash at C, and the
29             # L, L and L are set. But that
30             # is a strange case and unsupported.
31              
32 5     5 1 631041 sub prepare_app($self) {
  5         14  
  5         11  
33              
34 5 50       30 if ( my $client = $self->client ) {
35 5         316 foreach my $init (
36             [qw/ histogram timing_ms timing /],
37             [qw/ increment increment /],
38             [qw/ set_add set_add /],
39             )
40             {
41 15         90 my ( $attr, @methods ) = $init->@*;
42 15 100       54 next if defined $self->$attr;
43 14     16   116 my $method = first { $client->can($_) } @methods;
  16         150  
44 14 100       154 warn "No $attr method found for client " . ref($client)
45             unless defined $method;
46 57         94 $self->$attr(
47 57     57   88 sub($env, @args) {
  57         189  
  57         92  
48 57 50       127 return unless defined $method;
49             try {
50 57         3818 $client->$method( grep { defined $_ } @args );
  135         350  
51             }
52             catch {
53 3         65 my ($e) = $_;
54 3 50       11 if (my $logger = $env->{'psgix.logger'}) {
55 3         13 $logger->( { message => $e, level => 'error' } );
56             }
57             else {
58 0         0 $env->{'psgi.errors'}->print($e);
59             }
60 57         301 };
61              
62             }
63 14         78 );
64             }
65             }
66              
67 5 100   14   73 if (my $attr = first { !is_coderef($self->$_) } qw/ histogram increment set_add /) {
  14         54  
68 1         19 die "$attr is not a coderef";
69             }
70              
71 4 100       104 if ( my $catch = $self->catch_errors ) {
72              
73 1 50       5 unless ( is_coderef($catch) ) {
74              
75 1         2 $self->catch_errors(
76 1     1   3 sub( $env, $error ) {
  1         3  
  1         2  
77 1 50       5 if ( my $logger = $env->{'psgix.logger'} ) {
78 1         7 $logger->( { level => 'error', message => $error } );
79             }
80             else {
81 0         0 $env->{'psgi.errors'}->print($error);
82             }
83 1         6 my $message = 'Internal Error';
84 1         28 return [ 500, [ 'Content-Type' => 'text/plain', 'Content-Length' => length($message) ], [$message] ];
85             }
86 1         3 );
87              
88             }
89              
90             }
91             }
92              
93 7     7 1 308229 sub call ( $self, $env ) {
  7         20  
  7         40  
  7         13  
94              
95 7   33     135 my $client = ( $env->{'psgix.monitor.statsd'} //= $self->client );
96              
97 7         119 my $start = [Time::HiRes::gettimeofday];
98 7         41 my $res;
99              
100 7 100       29 if (my $catch = $self->catch_errors) {
101             try {
102 1     1   100 $res = $self->app->($env);
103             }
104             catch {
105 1     1   189 $res = $catch->( $env, $_ );
106             }
107 1         17 }
108             else {
109 6         64 $res = $self->app->($env);
110             }
111              
112 7         14 return Plack::Util::response_cb(
113             $res,
114 7     7   88 sub($res) {
  7         12  
115 7 50       37 return unless $client;
116              
117 7         37 my $rate = $self->sample_rate;
118              
119 7 50 33     113 $rate = undef if ( defined $rate ) && ( $rate >= 1 );
120              
121 7         31 my $histogram = $self->histogram;
122 7         49 my $increment = $self->increment;
123 7         43 my $set_add = $self->set_add;
124              
125 7         52 my $elapsed = Time::HiRes::tv_interval($start);
126              
127 7         179 $histogram->( $env, 'psgi.response.time', $elapsed * 1000, $rate );
128              
129 7 50       215 if ( defined $env->{CONTENT_LENGTH} ) {
130             $histogram->( $env,
131             'psgi.request.content-length',
132 7         22 $env->{CONTENT_LENGTH}, $rate
133             );
134             }
135              
136 7 50       182 if ( my $method = $env->{REQUEST_METHOD} ) {
137 7 100       45 $method = "other" unless $method =~ /^\w+$/a;
138 7         29 $increment->( $env, 'psgi.request.method.' . $method, $rate );
139             }
140              
141 7 100       139 if ( my $type = _mime_type_to_metric( $env->{CONTENT_TYPE} ) ) {
142 1         5 $increment->( $env, 'psgi.request.content-type.' . $type, $rate );
143             }
144              
145             $set_add->( $env, 'psgi.request.remote_addr', $env->{REMOTE_ADDR} )
146 7 50       75 if $env->{REMOTE_ADDR};
147              
148 7         151 $set_add->( $env, 'psgi.worker.pid', $$ );
149              
150 7         148 my $h = Plack::Util::headers( $res->[1] );
151              
152             my $xsendfile =
153             $env->{'plack.xsendfile.type'}
154             || $ENV{HTTP_X_SENDFILE_TYPE}
155 7   50     190 || 'X-Sendfile';
156              
157 7 50       58 if ( $h->exists($xsendfile) ) {
158 0         0 $increment->( $env, 'psgi.response.x-sendfile', $rate );
159             }
160              
161 7 50       230 if ( $h->exists('Content-Length') ) {
162 7   100     174 my $length = $h->get('Content-Length') || 0;
163 7         206 $histogram->( $env, 'psgi.response.content-length', $length, $rate );
164             }
165              
166 7 50       167 if ( my $type = _mime_type_to_metric( $h->get('Content-Type') ) ) {
167 7         33 $increment->( $env, 'psgi.response.content-type.' . $type, $rate );
168             }
169              
170 7         161 $increment->( $env, 'psgi.response.status.' . $res->[0], $rate );
171              
172 7 50       135 if (
    50          
173             $env->{'psgix.harakiri.supported'}
174             ? $env->{'psgix.harakiri'}
175             : $env->{'psgix.harakiri.commit'}
176             )
177             {
178 0         0 $increment->( $env, 'psgix.harakiri' ); # rate == 1
179             }
180              
181 7 50       42 $client->flush if $client->can('flush');
182              
183 7         82 return;
184             }
185 7         2053 );
186              
187             }
188              
189 14     14   248 sub _mime_type_to_metric( $type = undef ) {
  14         33  
  14         26  
190 14 100       45 return unless $type;
191 8 50       88 return unless $type =~ m#^\w+/(?:\w+[\-\+])*\w+(?: *;\w+=\w+)?#a;
192 8         117 return $type =~ s#\.#-#gr =~ s#/#.#gr =~ s/;.*$//r;
193             }
194              
195              
196             1;
197              
198             __END__