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