File Coverage

blib/lib/Parse/Apache/ServerStatus.pm
Criterion Covered Total %
statement 70 103 67.9
branch 21 46 45.6
condition 1 2 50.0
subroutine 11 17 64.7
pod 6 6 100.0
total 109 174 62.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Parse::Apache::ServerStatus - Simple module to parse apache's server-status.
4              
5             =head1 SYNOPSIS
6              
7             use Parse::Apache::ServerStatus;
8              
9             my $url = 'http://localhost/server-status';
10             # or http://localhost/server-status?auto
11              
12             my $prs = Parse::Apache::ServerStatus->new(
13             url => 'http://localhost/server-status',
14             timeout => 30
15             );
16              
17             my $stat = $prs->get or die $prs->errstr;
18              
19             # or
20              
21             my $prs = Parse::Apache::ServerStatus->new;
22              
23             foreach my $url (@urls) {
24             $prs->request(url => $url, timeout => 30) or die $prs->errstr;
25             my $stat = $prs->parse or die $prs->errstr;
26             }
27              
28             # or both in one step
29              
30             foreach my $url (@urls) {
31             my $stat = $prs->get(url => $url, timeout => 30)
32             or die $prs->errstr;
33             }
34              
35             =head1 DESCRIPTION
36              
37             This module parses the content of apache's server-status and countes the
38             current status by each process. It works nicely with apache versions 1.3
39             and 2.x.
40              
41             =head1 METHODS
42              
43             =head2 new()
44              
45             Call C to create a new Parse::Apache::ServerStatus object.
46              
47             =head2 request()
48              
49             This method requests the url and safes the content into the object.
50              
51             =head2 parse()
52              
53             Call C to parse the server status. This method returns a hash reference with
54             the parsed content. There are diffenrent keys that contains the following counts:
55              
56             p Parents (this key will be kicked in future releases, dont use it)
57             r Requests currenty being processed
58             i Idle workers
59             _ Waiting for Connection
60             S Starting up
61             R Reading Request
62             W Sending Reply
63             K Keepalive (read)
64             D DNS Lookup
65             C Closing connection
66             L Logging
67             G Gracefully finishing
68             I Idle cleanup of worker
69             . Open slot with no current process
70              
71             The following keys are set to 0 if extended server-status is not activated.
72              
73             ta Total accesses
74             tt Total traffic
75             rs Requests per second
76             bs Bytes per second
77             br Bytes per request
78              
79             It's possible to call C with the content as argument.
80              
81             my $stat = $prs->parse($content);
82              
83             If no argument is passed then C looks into the object for the content that is
84             stored by C.
85              
86             =head2 get()
87              
88             C calls C and C in one step. It's possible to set the options
89             C and C and it returns the hash reference that is returned by C.
90              
91             =head2 content()
92              
93             Call C if you need the full content of server-status.
94              
95             my $content = $prs->content;
96              
97             =head2 errstr()
98              
99             C contains the error string if the requests fails.
100              
101             =head2 ua()
102              
103             Access the C object if you want to set your own properties.
104              
105             =head1 OPTIONS
106              
107             There are only two options: C and C.
108              
109             Set C with the complete url like C.
110             There is only http supported by default, not https or other protocols.
111              
112             Set C to define the time in seconds to abort the request if there is no
113             response. The default is set to 180 seconds if the options isn't set.
114              
115             =head1 EXAMPLE
116              
117             use strict;
118             use warnings;
119             use Parse::Apache::ServerStatus;
120            
121             $|++;
122             my $prs = Parse::Apache::ServerStatus->new(
123             url => 'http://localhost/server-status',
124             # url => 'http://localhost/server-status?auto',
125             timeout => 10
126             );
127            
128             my @order = qw/p r i _ S R W K D C L G I . ta tt rs bs br/;
129             my $interval = 10;
130             my $header = 20;
131            
132             while ( 1 ) {
133             print map { sprintf("%8s", $_) } @order;
134             print "\n";
135             for (my $i = 0; $i <= $header; $i++) {
136             my $stat = $prs->get or die $prs->errstr;
137             print map { sprintf("%8s", $stat->{$_}) } @order;
138             print "\n";
139             sleep($interval);
140             }
141             }
142              
143             =head1 EXAMPLE CONFIGURATION FOR APACHE
144              
145             This is just an example to activate the handler server-status for localhost.
146              
147             ExtendedStatus On
148            
149             SetHandler server-status
150             Order Deny,Allow
151             Deny from all
152             Allow from localhost
153            
154              
155             into the configuration file.
156              
157             =head1 PREREQUISITES
158              
159             LWP::UserAgent
160             Params::Validate
161             Class::Accessor::Fast
162              
163             =head1 EXPORTS
164              
165             No exports.
166              
167             =head1 REPORT BUGS
168              
169             Please report all bugs to .
170              
171             =head1 AUTHOR
172              
173             Jonny Schulz .
174              
175             =head1 COPYRIGHT
176              
177             Copyright (C) 2007-2010 by Jonny Schulz. All rights reserved.
178              
179             This program is free software; you can redistribute it and/or
180             modify it under the same terms as Perl itself.
181              
182             =cut
183              
184             package Parse::Apache::ServerStatus;
185             our $VERSION = '0.10';
186              
187 2     2   56177 use strict;
  2         5  
  2         74  
188 2     2   11 use warnings;
  2         4  
  2         55  
189 2     2   2112 use LWP::UserAgent;
  2         121986  
  2         75  
190 2     2   1908 use Params::Validate;
  2         21746  
  2         136  
191 2     2   16 use base qw/Class::Accessor::Fast/;
  2         4  
  2         2808  
192             __PACKAGE__->mk_accessors(qw/ua/);
193 2     2   7387 use vars qw/$ERRSTR/;
  2         5  
  2         3698  
194             $ERRSTR = defined;
195              
196             sub new {
197 1   50 1 1 27 my $class = shift || __PACKAGE__;
198 1         3 my $self = bless { }, $class;
199              
200             # Total Accesses: 5
201             # Total kBytes: 8
202             # Uptime: 70
203             # ReqPerSec: .0714286
204             # BytesPerSec: 117.029
205             # BytesPerReq: 1638.4
206             # BusyWorkers: 2
207             # IdleWorkers: 10
208             # Scoreboard: ___WW_______.........
209              
210 1         18 $self->{rxauto} = qr/
211             (?:
212             Total\s+Accesses:\s*(\d+).*
213             Total\s+kBytes:\s*([\d\.]+).*
214             ReqPerSec:\s*([\d\.]+).*
215             BytesPerSec:\s*([\d\.]+).*
216             BytesPerReq:\s*([\d\.]+).*
217             |)
218             BusyWorkers:\s*([\d\.]+).*
219             IdleWorkers:\s*(\d+).*
220             Scoreboard:\s*(.+)
221             /xsi;
222              
223             # EXAMPLE apache
224             #
225             #
226             # Apache Status
227             #
228             #

Apache Server Status for localhost

229             #
230             # Server Version: Apache/1.3.34 (Ubuntu)
231             # Server Built: Mar 8 2007 00:01:35
232             #
233             #
234             # Current Time: Monday, 27-Oct-2008 16:57:03 CET
235             # Restart Time: Monday, 27-Oct-2008 16:56:55 CET
236             # Parent Server Generation: 1
237             # Server uptime: 8 seconds
238             #
239             # 1 requests currently being processed, 5 idle servers
240             #
W_____.......................................................... 
241             # ................................................................
242             # ................................................................
243             #
244              
245             # EXAMPLE apache extended
246             # Server Version: Apache/1.3.34 (Ubuntu)
247             # Server Built: Mar 8 2007 00:01:35
248             #
249             # Current Time: Saturday, 13-Oct-2007 20:41:00 CEST
250             # Restart Time: Saturday, 13-Oct-2007 20:30:09 CEST
251             # Parent Server Generation: 0
252             # Server uptime: 10 minutes 51 seconds
253             # Total accesses: 239409 - Total Traffic: 1.7 MB
254             # CPU Usage: u.32 s.21 cu0 cs0 - .0814% CPU load
255             # 368 requests/sec - 2733 B/second - 7 B/request
256             #
257             # 1 requests currently being processed, 32 idle servers
258             #
___________W____........._________________...................... 
259             # ................................................................
260             # ................................................................
261             #
262              
263 1         9 $self->{rx}->{1} = qr{
264             Parent\s+Server\s+Generation:\s+(\d+)\s+
.+?
265             (?:
266             Total\s+accesses:\s+(\d+)\s+\-\s+Total\s+Traffic:\s+([0-9\.]+\s+[kmg]{0,1}B)
.+
267             ([\d\.]+)\s+requests/sec\s+-\s+([\d\.]+\s+[kmg]{0,1}B)/second\s+-\s+([\d\.]+\s+[kmg]{0,1}B)/request
.+
268             ){0,1}
269             (\d+)\s+requests\s+currently\s+being\s+processed,\s+(\d+)\s+idle\s+servers.+?
270            
([_SRWKDCLGI.\n]+) 
271            
272             }xsi;
273              
274             # EXAMPLE apache2
275             #
276             #
277             # Apache Status
278             #
279             #

Apache Server Status for www.bloonix.de

280             #
281             #
Server Version: Apache/2.2.3 (Debian) mod_fastcgi/2.4.2 mod_ssl/2.2.3 OpenSSL/0.9.8c
282             #
Server Built: Mar 22 2008 09:29:10
283             #
284             #
Current Time: Monday, 27-Oct-2008 16:07:52 CET
285             #
Restart Time: Monday, 27-Oct-2008 16:07:46 CET
286             #
Parent Server Generation: 0
287             #
Server uptime: 6 seconds
288             #
1 requests currently being processed, 9 idle workers
289             #
W_________...................................................... 
290             # ................................................................
291             # ................................................................
292             # ................................................................
293             #
294              
295             # EXAMPLE apache2 extended
296             #
Server Version: Apache/2.2.3 (Debian) mod_fastcgi/2.4.2 mod_ssl/2.2.3 OpenSSL/0.9.8c
297             #
Server Built: Jun 17 2007 20:24:06
298             #
299             #
Current Time: Saturday, 13-Oct-2007 19:30:20 CEST
300             #
Restart Time: Thursday, 11-Oct-2007 18:00:42 CEST
301             #
Parent Server Generation: 0
302             #
Server uptime: 2 days 1 hour 29 minutes 38 seconds
303             #
Total accesses: 845 - Total Traffic: 3.8 MB
304             #
CPU Usage: u.26 s.06 cu0 cs0 - .00018% CPU load
305             #
.00474 requests/sec - 22 B/second - 4758 B/request
306             #
1 requests currently being processed, 9 idle workers
307             #
_.__.__W__.__................................................... 
308             # ................................................................
309             # ................................................................
310             # ................................................................
311             #
312              
313 1         6 $self->{rx}->{2} = qr{
314            
Parent\s+Server\s+Generation:\s+(\d+)
.+?
315             (?:
316             Total\s+accesses:\s+(\d+)\s+\-\s+Total\s+Traffic:\s+([0-9\.]+\s+[kmg]{0,1}B).+
317            
([\d\.]+)\s+requests/sec\s+-\s+([\d\.]+\s+[kmg]{0,1}B)/second\s+-\s+([\d\.]+\s+[kmg]{0,1}B)/request
.+
318             ){0,1}
319            
(\d+)\s+requests\s+currently\s+being\s+processed,\s+(\d+)\s+idle\s+workers
.+
320            
([_SRWKDCLGI\.\s\n]+) 
321            
322             }xsi;
323              
324 1         10 $self->ua(LWP::UserAgent->new);
325 1         3875 $self->ua->protocols_allowed(['http']);
326 1 50       28 $self->_set(@_) if @_;
327 1         3 return $self;
328             }
329              
330             sub get {
331 0     0 1 0 my $self = shift;
332 0 0       0 $self->request(@_) or return undef;
333 0         0 return $self->parse;
334             }
335              
336             sub request {
337 0     0 1 0 my $self = shift;
338 0 0       0 $self->_set(@_) if @_;
339              
340 0 0       0 if (!$self->{url}) {
341 0         0 return $self->_raise_error("missing mandatory option 'url'");
342             }
343              
344 0         0 $self->ua->timeout($self->{timeout});
345 0         0 my $response = $self->ua->get($self->{url});
346              
347 0 0       0 if (!$response->is_success()) {
348 0         0 return $self->_raise_error($response->status_line());
349             }
350              
351 0         0 $self->{content} = $response->content();
352 0 0       0 return $self->{content} ? 1 : undef;
353             }
354              
355 0     0 1 0 sub content { $_[0]->{content} }
356              
357             sub parse {
358 6     6 1 9983 my $self = shift;
359 6 50       24 my $content = $_[0] ? shift : $self->{content};
360              
361 6 50       17 if (!$content) {
362 0         0 return $self->_raise_error("no content received");
363             }
364              
365 6 100       25 if ($content =~ /^(?:Total|BusyWorkers)/) {
366 2         8 return $self->_parse_auto($content);
367             }
368              
369 4         10 my $version = $self->_version($content);
370              
371 4 50       10 if (!$version) {
372 0         0 return $self->_raise_error("unable to match the server version of apache");
373             }
374              
375 4         8 my $regex = $self->{rx};
376 4         7 my %data = ();
377 4         5 my $rest = ();
378              
379 4 50       9 if (!exists $regex->{$version}) {
380 0         0 return $self->_raise_error("apache/$version is not supported");
381             }
382              
383 4         907 ( $data{p}
384             , $data{ta}
385             , $data{tt}
386             , $data{rs}
387             , $data{bs}
388             , $data{br}
389             , $data{r}
390             , $data{i}
391             , $rest
392             ) = $content =~ $regex->{$version};
393              
394 4 50       14 if (!$rest) {
395 0         0 return $self->_raise_error("the content couldn't be parsed");
396             }
397              
398 4         386 $data{$_}++ for (split //, $rest);
399 4         70 delete $data{"\n"};
400 4         12 return $self->_data(\%data)
401             }
402              
403 0     0 1 0 sub errstr { $ERRSTR }
404              
405             #
406             # private stuff
407             #
408              
409             sub _version {
410 4     4   12 my ($self, $content) = @_;
411 4         5 my $version;
412              
413 4 50       23 if ($content =~ m{Server\s+Version:\s+Apache/(\d)}) {
414 4         23 $version = $1;
415             } else {
416 0         0 while (my ($v, $rx) = each %{$self->{rx}}) {
  0         0  
417 0 0       0 if ($content =~ $rx) {
418 0         0 $version = $v;
419 0         0 last;
420             }
421             }
422             }
423              
424 4         25 return $version;
425             }
426              
427             sub _data {
428 6     6   8 my ($self, $data) = @_;
429              
430 6         14 foreach my $key (qw/p r i _ S R W K D C L G I . ta tt rs/) {
431 102 100       190 if (!defined $data->{$key}) {
432 59         99 $data->{$key} = 0;
433             }
434             }
435              
436 6         11 foreach my $key (qw/bs br/) {
437 12 100       48 if (!defined $data->{$key}) {
    100          
438 6         9 $data->{$key} = 0;
439             } elsif ($data->{$key} =~ /^([\d\.]+)\s+([kmg]{0,1}B)/i) {
440 4         11 my ($s, $b) = ($1, $2);
441 4         7 $b = lc($b);
442              
443 4 50       15 if ($b eq 'b') {
    100          
    50          
    0          
444 0         0 $data->{$key} = $s;
445             } elsif ($b eq 'kb') {
446 2         7 $data->{$key} = $s * 1024;
447             } elsif ($b eq 'mb') {
448 2         9 $data->{$key} = $s * 1048576;
449             } elsif ($b eq 'gb') {
450 0         0 $data->{$key} = $s * 1073741824;
451             }
452             }
453             }
454              
455 6         23 return $data;
456             }
457              
458             sub _parse_auto {
459 2     2   3 my $self = shift;
460 2 50       6 my $content = $_[0] ? shift : $self->{content};
461 2         3 my %data = ();
462 2         3 my $rest = '';
463              
464 2         60 ( $data{ta}
465             , $data{tt}
466             , $data{rs}
467             , $data{bs}
468             , $data{br}
469             , $data{r}
470             , $data{i}
471             , $rest
472             ) = $content =~ $self->{rxauto};
473              
474 2 50       9 if (!$rest) {
475 0         0 return $self->_raise_error("the content couldn't be parsed");
476             }
477              
478 2         34 $data{$_}++ for (split //, $rest);
479 2         8 delete $data{"\n"};
480 2         8 return $self->_data(\%data);
481             }
482              
483             sub _set {
484 0     0     my $self = shift;
485 0           my %opts = Params::Validate::validate(@_, {
486             url => {
487             type => Params::Validate::SCALAR,
488             regex => qr{^http://.+},
489             },
490             timeout => {
491             type => Params::Validate::SCALAR,
492             regex => qr/^\d+\z/,
493             default => 180,
494             },
495             });
496 0           $self->{url} = $opts{url};
497 0           $self->{timeout} = $opts{timeout};
498             }
499              
500             sub _raise_error {
501 0     0     $ERRSTR = $_[1];
502 0           return undef;
503             }
504              
505             1;