File Coverage

blib/lib/Tail/Stat/Plugin/nginx.pm
Criterion Covered Total %
statement 12 80 15.0
branch 0 42 0.0
condition 0 17 0.0
subroutine 4 9 44.4
pod 5 5 100.0
total 21 153 13.7


line stmt bran cond sub pod time code
1             package Tail::Stat::Plugin::nginx;
2              
3             =head1 NAME
4              
5             Tail::Stat::Plugin::nginx - Statistics collector for Nginx web-server
6              
7             =cut
8              
9 1     1   1171 use strict;
  1         1  
  1         27  
10 1     1   5 use warnings qw(all);
  1         3  
  1         65  
11              
12              
13             =head1 SYNOPSIS
14              
15             tstatd -o clf nginx httpd.access_log
16              
17              
18             =head1 LOG FORMATS
19              
20             Nginx has predefined (compiled in) log format named B.
21             This format defined as:
22              
23             log_format combined '$remote_addr - $remote_user [$time_local] '
24             '"$request" $status $body_bytes_sent '
25             '"$http_referer" "$http_user_agent"';
26              
27             This format produces such lines:
28              
29             1.2.3.4 - - [22/Jan/2010:19:34:21 +0300] "GET /foo/ HTTP/1.1" 200 11078 "http://www.rambler.ru/" "Mozilla/5.0 (Windows; U; Windows NT 5.1"
30              
31             For collecting statistics about request timing, upstream delays and
32             cache efficiency it's recommended to extend log format:
33              
34             log_format extended '$remote_addr - $remote_user [$time_local] '
35             '"$request" $status $body_bytes_sent '
36             '"$http_referer" "$http_user_agent" '
37             'rt=$request_time ut="$upstream_response_time" '
38             'cs=$upstream_cache_status';
39              
40             Extended format creates log records like this:
41              
42             1.2.3.4 - - [22/Jan/2010:19:34:21 +0300] "GET /foo/ HTTP/1.1" 200 11078 "http://www.rambler.ru/" "Mozilla/5.0 (Windows; U; Windows NT 5.1" rt=0.010 ut="2.001, 0.345" cs=MISS
43              
44              
45             =head1 OPTIONS
46              
47             =over
48              
49             =item C
50              
51             Simplify regular expression (don't accumulate information about client/upstream timings and cache status).
52              
53             =back
54              
55              
56             =head1 STATISTICS
57              
58             =head2 Overall statistics
59              
60              
61             =head3 HTTP traffic
62              
63             =over
64              
65             =item C
66              
67             Total number of served requests.
68              
69             =item C
70              
71             Total number of sent bytes.
72              
73             =item C
74              
75             Total number of malformed requests.
76              
77             =back
78              
79              
80             =head3 HTTP methods
81              
82             =over
83              
84             =item C
85              
86             Total number of served HEAD requests.
87              
88             =item C
89              
90             Total number of served GET requests.
91              
92             =item C
93              
94             Total number of served subrequests.
95              
96             =item C
97              
98             Total number of served POST requests.
99              
100             =item C
101              
102             Total number of served another types of requests.
103              
104             =back
105              
106              
107             =head3 HTTP versions
108              
109             =over
110              
111             =item C
112              
113             Total number of served HTTP/0.9 requests.
114              
115             =item C
116              
117             Total number of served HTTP/1.0 requests.
118              
119             =item C
120              
121             Total number of served HTTP/1.1 requests.
122              
123             =back
124              
125              
126             =head3 HTTP statuses
127              
128             =over
129              
130             =item C
131              
132             Total number of served requests with status of xxx.
133              
134             =item C
135              
136             Total number of served requests with status of 100-199.
137              
138             =item C
139              
140             Total number of served requests with status of 200-299.
141              
142             =item C
143              
144             Total number of served requests with status of 300-399.
145              
146             =item C
147              
148             Total number of served requests with status of 400-499.
149              
150             =item C
151              
152             Total number of served requests with status of 500-599.
153              
154             =back
155              
156              
157             =head3 Cache statuses
158              
159             B available unless C option in use.
160              
161             =over
162              
163             =item C
164              
165             Total number of served request with cache status EXPIRED.
166              
167             =item C
168              
169             Total number of served request with cache status HIT.
170              
171             =item C
172              
173             Total number of served request with cache status MISS.
174              
175             =item C
176              
177             Total number of served request with cache status STALE.
178              
179             =item C
180              
181             Total number of served request with cache status UPDATING.
182              
183             =back
184              
185              
186             =head3 Upstream requests
187              
188             B available unless C option in use.
189              
190             =over
191              
192             =item C
193              
194             Total number of requests proxied to upstreams.
195              
196             =item C
197              
198             Total number of switching between upstreams.
199              
200             =back
201              
202              
203             =head2 Last statistics
204              
205              
206             =head3 HTTP traffic
207              
208             =over
209              
210             =item C
211              
212             Total number of served requests during last window.
213              
214             =item C
215              
216             Total number of sent bytes during last window.
217              
218             =item C
219              
220             Total amount of time elapsed to serve requests during last window.
221              
222             B available unless C option in use.
223              
224             =back
225              
226              
227             =head3 Upstream requests
228              
229             B available unless C option in use.
230              
231             =over
232              
233             =item C
234              
235             Total number of proxied requests during last window.
236              
237             =item C
238              
239             Total amount of time elapsed to proxying requests during last window.
240              
241             =back
242              
243             =cut
244              
245              
246 1     1   5 use base qw(Tail::Stat::Plugin);
  1         2  
  1         66  
247 1     1   5 use List::Util qw(sum);
  1         2  
  1         1268  
248              
249              
250             sub regex {
251 0     0 1   my $self = shift;
252              
253 0           my $re = qr{
254             ^
255             (\S+) # remote address [0]
256             \s
257             (\S+) # ident [1]
258             \s
259             (\S+) # remote user [2]
260             \s
261             \[([^\]]+)\] # time local [3]
262             \s
263             "(?: # request
264             ([A-Z]+) # http method [4]
265             \s+
266             (/[^"]*?) # request uri [5]
267             (?: # http version > 0.9
268             \s+
269             HTTP/(1\.[01]) # http version [6]
270             )?
271             |
272             ([^"]*) # malformed request [7]
273             )"
274             \s
275             ([1-5]\d{2}) # http status [8]
276             \s
277             (\d+|-) # content length [9]
278             \s
279             "([^"]*)" # referrer [10]
280             \s
281             "([^"]*)" # user agent [11]
282             }x;
283             $re .= qr{
284             (?:
285             \srt=([\d\.]+) # request time [12]
286             |
287             \sut="([^"]+)" # upstream response time [13]
288             |
289             \scs=(\S+) # upstream cache status [14]
290             |
291             .
292             )*
293 0 0         }x unless $self->{clf};
294              
295 0           return $re;
296             }
297              
298              
299             sub process_data {
300 0     0 1   my $self = shift;
301 0           my ($ref,$pub,$prv,$win) = @_;
302              
303             # don't take into account at all
304 0 0 0       return 1 if $ref->[8]==400 || $ref->[8]==408;
305              
306 0           $pub->{http_request}++;
307 0           $win->{http_request}++;
308              
309 0 0         if ($ref->[4]) { # method
310 0           $pub->{'http_method_'.$ref->[4]}++;
311 0 0         if ($ref->[6]) { # version
    0          
312 0           $pub->{'http_version_'.$ref->[6]}++;
313             } elsif ($ref->[4] ne 'INC') {
314 0           $pub->{'http_version_0.9'}++;
315             }
316             } else {
317 0           $pub->{malformed_request}++;
318             }
319              
320 0           $pub->{'http_status_'.$ref->[8]}++;
321              
322 0 0         unless ($ref->[9] eq '-') {
323 0           $pub->{http_byte} += $ref->[9];
324 0           $win->{http_byte} += $ref->[9];
325             }
326              
327             # extended part
328 0 0         unless ($self->{clf}) {
329 0 0 0       $win->{request_time} += $ref->[12]
330             if $ref->[12] &&
331             $ref->[12] < 1_000_000; # old nginx bug workaround
332              
333 0 0 0       if ($ref->[13] && $ref->[13] ne '-') {
334 0 0 0       if (index($ref->[13], ',') >= 0 ||
335             index($ref->[13], ' : ') >= 0 ) { # several upstreams
336 0           my @ut = grep { $_ ne '-' } split '(?:, | : )', $ref->[13];
  0            
337 0           $pub->{upstream_request} += @ut;
338 0           $win->{upstream_request} += @ut;
339 0 0         $pub->{upstream_next} += $#ut if @ut;
340 0           $win->{upstream_time} += $_ for @ut;
341             } else { # single upstream
342 0           $pub->{upstream_request}++;
343 0           $win->{upstream_request}++;
344 0           $win->{upstream_time} += $ref->[13];
345             }
346             }
347              
348 0 0 0       $pub->{'cache_status_'.$ref->[14]}++ if $ref->[14] && $ref->[14] ne '-';
349             }
350              
351 0           return 1;
352             }
353              
354              
355             sub process_window {
356 0     0 1   my $self = shift;
357 0           my ($pub,$prv,$wins) = @_;
358              
359 0 0         for my $x (
360             qw( http_request http_byte ),
361             $self->{clf} ?
362             () :
363             qw( request_time upstream_request upstream_time )
364             ) {
365 0   0       $pub->{'last_'.$x} = sum ( map { $_->{$x} || 0 } @$wins ) || 0;
366             }
367             }
368              
369              
370             sub stats_zone {
371 0     0 1   my ($self,$zone,$pub,$prv,$wins) = @_;
372              
373             # required keys defaults
374 0           my %out = map { $_ => 0 } qw(
375              
376             malformed_request
377              
378             http_request
379             last_http_request
380              
381             http_byte
382             last_http_byte
383              
384             http_method_head
385             http_method_get
386             http_method_inc
387             http_method_post
388             http_method_other
389              
390             http_version_0_9
391             http_version_1_0
392             http_version_1_1
393              
394             http_status_1xx
395             http_status_2xx
396             http_status_3xx
397             http_status_4xx
398             http_status_5xx
399              
400 0 0         ), $self->{clf} ? () : qw(
401              
402             last_request_time
403             last_upstream_time
404              
405             upstream_request
406             last_upstream_request
407              
408             upstream_next
409              
410             cache_status_expired
411             cache_status_hit
412             cache_status_miss
413             cache_status_stale
414             cache_status_updating
415              
416             );
417              
418              
419             # agregate required keys
420 0           for ( keys %$pub ) {
421              
422             # http_method
423 0 0         /^http_method_(HEAD|GET|INC|POST)/ and do {
424 0           $out{'http_method_'. lc $1} += $pub->{$_};
425 0           next;
426             };
427 0 0         /^http_method_/ and do {
428 0           $out{'http_method_other'} += $pub->{$_};
429 0           next;
430             };
431              
432             # http_status
433 0 0         /^http_status_([1-5])/ and do {
434 0           $out{'http_status_'. $1.'xx'} += $pub->{$_};
435             # particular statuses
436 0           $out{$_} += $pub->{$_};
437 0           next;
438             };
439              
440             # http_version
441 0 0         /^http_version_(\S+)/ and do {
442 0           (my $v = $1) =~ s/\./_/g;
443 0           $out{'http_version_'. $v} += $pub->{$_};
444 0           next;
445             };
446              
447             # extended mode
448 0 0         unless ($self->{clf}) {
449             # cache_status
450 0 0         /^cache_status_(\S+)/ and do {
451 0           $out{'cache_status_'. lc $1} += $pub->{$_};
452 0           next;
453             };
454              
455             # copy remainings values as is
456 0           $out{$_} += $pub->{$_};
457              
458 0           next;
459             }
460              
461             # simple attributes
462             /^(?:
463             malformed_request |
464             http_request |
465             last_http_request |
466             http_byte |
467             last_http_byte
468 0 0         )$/x and do {
469 0           $out{$_} += $pub->{$_};
470 0           next;
471             };
472             }
473              
474 0           map { $_.': '.$out{$_} } sort keys %out;
  0            
475             }
476              
477              
478             sub parse_error {
479 0     0 1   'notice'
480             }
481              
482              
483             =head1 AUTHOR
484              
485             Oleg A. Mamontov, C<< >>
486              
487              
488             =head1 COPYRIGHT
489              
490             This program is free software; you can redistribute it and/or modify it
491             under the terms of either: the GNU General Public License as published
492             by the Free Software Foundation; or the Artistic License.
493              
494             See http://dev.perl.org/licenses/ for more information.
495              
496             =cut
497              
498              
499             1;
500