———package
Tail::Stat::Plugin::nginx;
=head1 NAME
Tail::Stat::Plugin::nginx - Statistics collector for Nginx web-server
=cut
use
strict;
=head1 SYNOPSIS
tstatd -o clf nginx httpd.access_log
=head1 LOG FORMATS
Nginx has predefined (compiled in) log format named B<combined>.
This format defined as:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent"';
This format produces such lines:
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"
For collecting statistics about request timing, upstream delays and
cache efficiency it's recommended to extend log format:
log_format extended '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time ut="$upstream_response_time" '
'cs=$upstream_cache_status';
Extended format creates log records like this:
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
=head1 OPTIONS
=over
=item C<clf>
Simplify regular expression (don't accumulate information about client/upstream timings and cache status).
=back
=head1 STATISTICS
=head2 Overall statistics
=head3 HTTP traffic
=over
=item C<http_request>
Total number of served requests.
=item C<http_byte>
Total number of sent bytes.
=item C<malformed_request>
Total number of malformed requests.
=back
=head3 HTTP methods
=over
=item C<http_method_head>
Total number of served HEAD requests.
=item C<http_method_get>
Total number of served GET requests.
=item C<http_method_inc>
Total number of served subrequests.
=item C<http_method_post>
Total number of served POST requests.
=item C<http_method_other>
Total number of served another types of requests.
=back
=head3 HTTP versions
=over
=item C<http_version_0_9>
Total number of served HTTP/0.9 requests.
=item C<http_version_1_0>
Total number of served HTTP/1.0 requests.
=item C<http_version_1_1>
Total number of served HTTP/1.1 requests.
=back
=head3 HTTP statuses
=over
=item C<http_status_1xx>
Total number of served requests with status of 100-199.
=item C<http_status_2xx>
Total number of served requests with status of 200-299.
=item C<http_status_3xx>
Total number of served requests with status of 300-399.
=item C<http_status_4xx>
Total number of served requests with status of 400-499.
=item C<http_status_5xx>
Total number of served requests with status of 500-599.
=item C<http_status_404>
Total number of served requests with status of 404.
=item C<http_status_499>
Total number of served requests with status of 499.
=item C<http_status_500>
Total number of served requests with status of 500.
=item C<http_status_502>
Total number of served requests with status of 502.
=back
=head3 Cache statuses
B<NOTE:> available unless C<clf> option in use.
=over
=item C<cache_status_expired>
Total number of served request with cache status EXPIRED.
=item C<cache_status_hit>
Total number of served request with cache status HIT.
=item C<cache_status_miss>
Total number of served request with cache status MISS.
=item C<cache_status_stale>
Total number of served request with cache status STALE.
=item C<cache_status_updating>
Total number of served request with cache status UPDATING.
=back
=head3 Upstream requests
B<NOTE:> available unless C<clf> option in use.
=over
=item C<upstream_request>
Total number of requests proxied to upstreams.
=item C<upstream_next_count>
Total number of switching between upstreams.
=back
=head2 Last statistics
=head3 HTTP traffic
=over
=item C<last_http_request>
Total number of served requests during last window.
=item C<last_http_byte>
Total number of sent bytes during last window.
=item C<last_request_time>
Total amount of time elapsed to serve requests during last window.
B<NOTE:> available unless C<clf> option in use.
=back
=head3 Upstream requests
B<NOTE:> available unless C<clf> option in use.
=over
=item C<last_upstream_request>
Total number of proxied requests during last window.
=item C<last_upstream_time>
Total amount of time elapsed to proxying requests during last window.
=back
=cut
sub
regex {
my
$self
=
shift
;
my
$re
=
qr{
^
(\S+) # remote address [0]
\s
(\S+) # ident [1]
\s
(\S+) # remote user [2]
\s
\[([^\]]+)\] # time local [3]
\s
"(?: # request
([A-Z]+) # http method [4]
\s+
( # request uri [5]
/\S* # abs_path
|
http://\S* # absoluteURI
)
(?: # http version > 0.9
\s+
HTTP/(1\.[01]) # http version [6]
)?
|
([^"]*) # malformed request [7]
)"
\s
([1-5]\d{2}
)
# http status [8]
\s
(\d+|-)
# content length [9]
\s
"([^"
]*)"
# referrer [10]
\s
"([^"
]*)"
# user agent [11]
}x;
$re
.=
qr{
(?:
rt=([\d\.]+) # request time [12]
|
ut="([^"]+)" # upstream response time [13]
|
cs=(\S+) # upstream cache status [14]
|
.
)*
}
x
unless
$self
->{clf};
return
$re
;
}
sub
process_data {
my
$self
=
shift
;
my
(
$ref
,
$pub
,
$prv
,
$win
) =
@_
;
$pub
->{http_request}++;
$win
->{http_request}++;
if
(
$ref
->[4]) {
# method
$pub
->{
'http_method_'
.
$ref
->[4]}++;
if
(
$ref
->[6]) {
# version
$pub
->{
'http_version_'
.
$ref
->[6]}++;
}
elsif
(
$ref
->[4] ne
'INC'
) {
$pub
->{
'http_version_0.9'
}++;
}
}
else
{
$pub
->{malformed_request}++;
}
$pub
->{
'http_status_'
.
$ref
->[8]}++;
unless
(
$ref
->[9] eq
'-'
) {
$pub
->{http_byte} +=
$ref
->[9];
$win
->{http_byte} +=
$ref
->[9];
}
# extended part
unless
(
$self
->{clf}) {
$win
->{request_time} +=
$ref
->[12]
if
$ref
->[12] &&
$ref
->[12] < 1_000_000;
if
(
$ref
->[13] &&
$ref
->[13] ne
'-'
) {
if
(
index
(
$ref
->[13],
','
) >= 0 ||
index
(
$ref
->[13],
' : '
) >= 0 ) {
# several upstreams
my
@ut
=
grep
{
$_
ne
'-'
}
split
'(?:, | : )'
,
$ref
->[13];
$pub
->{upstream_request} +=
@ut
;
$win
->{upstream_request} +=
@ut
;
$pub
->{upstream_next} +=
$#ut
if
@ut
;
$win
->{upstream_time} +=
$_
for
@ut
;
}
else
{
# single upstream
$pub
->{upstream_request}++;
$win
->{upstream_request}++;
$win
->{upstream_time} +=
$ref
->[13];
}
}
$pub
->{
'cache_status_'
.
$ref
->[14]}++
if
$ref
->[14] &&
$ref
->[14] ne
'-'
;
}
return
1;
}
sub
process_window {
my
$self
=
shift
;
my
(
$pub
,
$prv
,
$wins
) =
@_
;
for
my
$x
(
qw( http_request http_byte )
,
$self
->{clf} ?
() :
qw( request_time upstream_request upstream_time )
) {
$pub
->{
'last_'
.
$x
} = sum (
map
{
$_
->{
$x
} || 0 }
@$wins
) || 0;
}
}
sub
stats_zone {
my
(
$self
,
$zone
,
$pub
,
$prv
,
$wins
) =
@_
;
# required keys defaults
my
%out
=
map
{
$_
=> 0 }
qw(
malformed_request
http_request
last_http_request
http_byte
last_http_byte
http_method_head
http_method_get
http_method_inc
http_method_post
http_method_other
http_version_0_9
http_version_1_0
http_version_1_1
http_status_1xx
http_status_2xx
http_status_3xx
http_status_4xx
http_status_5xx
http_status_404
http_status_499
http_status_500
http_status_502
)
,
$self
->{clf} ? () :
qw(
last_request_time
last_upstream_time
upstream_request
last_upstream_request
upstream_next
cache_status_expired
cache_status_hit
cache_status_miss
cache_status_stale
cache_status_updating
)
;
# agregate required keys
for
(
keys
%$pub
) {
# http_method
/^http_method_(HEAD|GET|INC|POST)/ and
do
{
$out
{
'http_method_'
.
lc
$1} +=
$pub
->{
$_
};
next
;
};
/^http_method_/ and
do
{
$out
{
'http_method_other'
} +=
$pub
->{
$_
};
next
;
};
# http_status
/^http_status_([1-5])/ and
do
{
$out
{
'http_status_'
. $1.
'xx'
} +=
$pub
->{
$_
};
# particular statuses
/^http_status_(404|499|500|502)/ and
do
{
$out
{
'http_status_'
. $1} +=
$pub
->{
$_
};
};
next
;
};
# http_version
/^http_version_(\S+)/ and
do
{
(
my
$v
= $1) =~ s/\./_/g;
$out
{
'http_version_'
.
$v
} +=
$pub
->{
$_
};
next
;
};
# extended mode
unless
(
$self
->{clf}) {
# cache_status
/^cache_status_(\S+)/ and
do
{
$out
{
'cache_status_'
.
lc
$1} +=
$pub
->{
$_
};
next
;
};
# copy remainings values as is
$out
{
$_
} +=
$pub
->{
$_
};
next
;
}
# simple attributes
/^(?:
malformed_request |
http_request |
last_http_request |
http_byte |
last_http_byte
)$/x and
do
{
$out
{
$_
} +=
$pub
->{
$_
};
next
;
};
}
map
{
$_
.
': '
.
$out
{
$_
} }
sort
keys
%out
;
}
sub
parse_error {
'notice'
}
=head1 AUTHOR
Oleg A. Mamontov, C<< <oleg@mamontov.net> >>
=head1 COPYRIGHT
This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.
See http://dev.perl.org/licenses/ for more information.
=cut
1;