File Coverage

blib/lib/Data/Apache/mod_status.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package Data::Apache::mod_status;
2              
3             =head1 NAME
4              
5             Data::Apache::mod_status - get values from Apache mod_status page
6              
7             =head1 SYNOPSIS
8              
9             use Data::Apache::mod_status;
10            
11             my $mod_status = Data::Apache::mod_status->new(
12             'url' => $url,
13             )->refresh;
14             my $info = $mod_status->info;
15             my $workers = $mod_status->workers;
16              
17             =head1 DESCRIPTION
18              
19             This module fetches page generated by C<Apache mod_status>, scrapes it's content
20             and returns values in a object properties so that the values can be processed
21             further.
22              
23             See L<script/mod_status-info> for command line script which can return this data
24             in a different formats - C<xml|data-dumper|ecsv|yaml|json|rrd>.
25              
26             =cut
27              
28 1     1   95447 use warnings;
  1         2  
  1         24  
29 1     1   3 use strict;
  1         1  
  1         24  
30              
31             our $VERSION = '0.02';
32              
33 1     1   459 use Moose;
  1         291986  
  1         6  
34 1     1   4552 use Moose::Util::TypeConstraints;
  1         1  
  1         7  
35 1     1   2017 use LWP::UserAgent;
  1         29250  
  1         29  
36 1     1   389 use Carp::Clan 'croak';
  1         1202  
  1         5  
37 1     1   557 use IPC::Run3 'run3';
  1         14253  
  1         50  
38 1     1   1148 use XML::LibXSLT;
  0            
  0            
39             use XML::LibXML;
40              
41             use Data::Apache::mod_status::2XML;
42             use Data::Apache::mod_status::Info;
43             use Data::Apache::mod_status::Workers;
44              
45              
46             =head1 PROPERTIES
47              
48             =cut
49              
50             subtype 'mod_status_Info'
51             => as 'Object'
52             => where { $_[0]->isa('Data::Apache::mod_status::Info') };
53              
54             subtype 'mod_status_Workers'
55             => as 'Object'
56             => where { $_[0]->isa('Data::Apache::mod_status::Workers') };
57              
58             subtype 'XML_LibXML_Document'
59             => as 'Object'
60             => where { $_[0]->isa('XML::LibXML::Document') };
61              
62             =head2 url
63              
64             URL where the apache mod status can be found
65              
66             =cut
67              
68             has 'url' => (
69             'is' => 'rw',
70             'isa' => 'Str',
71             'default' => 'http://localhost/server-status',
72             );
73              
74             =head2 xml_dom
75              
76             L<XML::LibXML::Document> of the C<mod_info> XML document. Loaded by C<refresh()>.
77              
78             =cut
79              
80             has 'xml_dom' => (
81             'is' => 'rw',
82             'isa' => 'XML_LibXML_Document',
83             );
84              
85             =head2 info
86              
87             L<Data::Apache::mod_status::Info> object
88              
89             =cut
90              
91             has 'info' => (
92             'is' => 'rw',
93             'isa' => 'mod_status_Info'
94             );
95              
96             =head2 workers
97              
98             L<Data::Apache::mod_status::Workers> object
99              
100             =cut
101              
102             has 'workers' => (
103             'is' => 'rw',
104             'isa' => 'mod_status_Workers'
105             );
106              
107             =head1 METHODS
108              
109             =head2 new()
110              
111             Object constructor.
112              
113             =head2 refresh()
114              
115             Fetches fresh C<mod_status> page and stores xml in C<xml_dom>.
116              
117             =cut
118              
119             sub refresh {
120             my $self = shift;
121              
122             my $mod_status_page = $self->_fetch_mod_status_page;
123            
124             my $tidy_mod_status_page;
125             my @tidy_cmd = (
126             'tidy',
127             '-w', '6000',
128             '-utf8',
129             '-asxhtml',
130             '-i',
131             '-f', '/dev/null',
132             );
133             eval { run3(\@tidy_cmd, \$mod_status_page, \$tidy_mod_status_page, undef, { 'return_if_system_error' => 1 }); };
134             die 'execution of tidy failed (not installed? `apt-get install tidy`)'
135             if (($? >> 8) > 1);
136            
137             # make the mod_status page to xml transformation
138             my $parser = XML::LibXML->new();
139             $parser->load_ext_dtd(0); # we don't need them, can just slow things down
140             my $xslt = XML::LibXSLT->new();
141             my $stylesheet = $xslt->parse_stylesheet(
142             $parser->parse_string(Data::Apache::mod_status::2XML->xslt()),
143             );
144             my $mod_status_dom = $stylesheet->transform(
145             $parser->parse_string($tidy_mod_status_page)
146             );
147             $self->xml_dom(
148             $parser->parse_string($stylesheet->output_string($mod_status_dom)),
149             );
150            
151             # update object properties
152             $self->_refresh_from_dom();
153            
154             # return self to allow chaining
155             return $self;
156             }
157              
158              
159             =head2 _refresh_from_dom()
160              
161             Called by C<refresh()> to populate C<workers> and C<info> properties.
162              
163             =cut
164              
165             sub _refresh_from_dom {
166             my $self = shift;
167            
168             my $dom = $self->xml_dom();
169            
170             # parse info lines
171             my $info = Data::Apache::mod_status::Info->new();
172             foreach my $info_line ($dom->findnodes('/mod_status/info_lines/line/text()')) {
173             $info_line = $info_line->toString;
174            
175             $info_line =~ m/^Server \s Version: \s (.+)$/xms
176             ? $info->server_version($1) :
177             $info_line =~ m/^Server \s Built: \s (.+)$/xms
178             ? $info->server_build_str($1) :
179             $info_line =~ m/^Current \s Time: \s (.+)$/xms
180             ? $info->current_time_str($1) :
181             $info_line =~ m/^Restart \s Time: \s (.+)$/xms
182             ? $info->restart_time_str($1) :
183             $info_line =~ m/^Parent \s Server \s Generation: \s (\d+)$/xms
184             ? $info->parent_server_generation($1) :
185             $info_line =~ m/^Server \s uptime: \s (.+)$/xms
186             ? $info->server_uptime_str($1) :
187             $info_line =~ m/^Total \s accesses: \s (\d+) \s - \s Total \s Traffic: \s (\d+(?:\.\d+)?\s.+)$/xms
188             ? ($info->total_accesses($1), $info->total_traffic_str($2)) :
189             $info_line =~ m/^CPU \s Usage: \s (.+)$/xms
190             ? $info->cpu_usage_str($1) :
191             $info_line =~ m{requests/sec \s - \s .+/second \s - \s .+/request$}xms
192             ? 1 :
193             $info_line =~ m/^(\d+) \s requests \s currently \s being \s processed, \s (\d+) \s idle \s workers$/xms
194             ? ($info->current_requests($1), $info->idle_workers($2))
195             : (die 'unknown mod_status info line "', $info_line, '"');
196             }
197            
198             # store new values
199             $self->info($info);
200            
201             my ($workers_tag) = $dom->findnodes('/mod_status/workers');
202             $self->workers(
203             Data::Apache::mod_status::Workers->new(
204             'workers_tag' => $workers_tag,
205             )
206             );
207            
208             return $self;
209             }
210              
211              
212             =head2 _fetch_mod_status_page()
213              
214             Method that fetches C<mod_status> page and returning it's content.
215              
216             =cut
217              
218             sub _fetch_mod_status_page {
219             my $self = shift;
220            
221             my $url = $self->url;
222             croak 'set url'
223             if not defined $url;
224            
225             # get mod_status page
226             my $ua = LWP::UserAgent->new;
227             $ua->timeout(10);
228             $ua->env_proxy;
229             my $response = $ua->get($url);
230             die 'failed to fetch "', $url, '" - '.$response->status_line
231             if $response->is_error();
232            
233             # tidy mod_status page to be xhtml document
234             return $response->decoded_content;
235             }
236              
237             "Zed's Dead, baby";
238              
239              
240             __END__
241              
242             =head1 SEE ALSO
243              
244             L<examples/rrd/> folder for examples how to create, update and graph C<mod_status>
245             data using L<Data::Apache::mod_status> and rrdtool.
246              
247             =head1 DEBIAN
248              
249             Build-Depends: debhelper (>= 5), libmodule-build-perl, libtest-differences-perl,
250             libtest-exception-perl, libfile-slurp-perl, tidy
251             Depends: ${perl:Depends}, ${misc:Depends}, libxml-libxslt-perl,
252             libdatetime-format-strptime-perl, libmoose-perl, libwww-perl, libcarp-clan-perl,
253             libxml-libxml-perl, libipc-run3-perl, libmoose-perl, tidy
254              
255             =head1 COPYRIGHT AND LICENSE
256              
257             This library is free software; you can redistribute it and/or modify
258             it under the same terms as Perl itself.
259              
260             =head1 AUTHOR
261              
262             Jozef Kutej
263              
264             =cut