File Coverage

blib/lib/Sys/Statistics/Linux.pm
Criterion Covered Total %
statement 61 81 75.3
branch 16 34 47.0
condition 2 8 25.0
subroutine 9 12 75.0
pod 6 6 100.0
total 94 141 66.6


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Sys::Statistics::Linux - Front-end module to collect system statistics
4              
5             =head1 SYNOPSIS
6              
7             use Sys::Statistics::Linux;
8              
9             my $lxs = Sys::Statistics::Linux->new(
10             sysinfo => 1,
11             cpustats => 1,
12             procstats => 1,
13             memstats => 1,
14             pgswstats => 1,
15             netstats => 1,
16             sockstats => 1,
17             diskstats => 1,
18             diskusage => 1,
19             loadavg => 1,
20             filestats => 1,
21             processes => 1,
22             );
23              
24             sleep 1;
25             my $stat = $lxs->get;
26              
27             =head1 DESCRIPTION
28              
29             Sys::Statistics::Linux is a front-end module and gather different linux system information
30             like processor workload, memory usage, network and disk statistics and a lot more. Refer the
31             documentation of the distribution modules to get more information about all possible statistics.
32              
33             =head1 MOTIVATION
34              
35             My motivation is very simple... every linux administrator knows the well-known tool sar of sysstat.
36             It helps me a lot of time to search for system bottlenecks and to solve problems, but it's hard to
37             parse the output if you want to store the statistics into a database. So I thought to develope
38             Sys::Statistics::Linux. It's not a replacement but it should make it simpler to you to write your
39             own system monitor.
40              
41             If Sys::Statistics::Linux doesn't provide statistics that are strongly needed then let me know it.
42              
43             =head1 TECHNICAL NOTE
44              
45             This distribution collects statistics by the virtual F filesystem (procfs) and is
46             developed on the default vanilla kernel. It is tested on x86 hardware with the distributions
47             RHEL, Fedora, Debian, Ubuntu, Asianux, Slackware, Mandriva and openSuSE (SLES on zSeries as
48             well but a long time ago) on kernel versions 2.4 and/or 2.6. It's possible that it doesn't
49             run on all linux distributions if some procfs features are deactivated or too much modified.
50             As example the linux kernel 2.4 can compiled with the option C what turn
51             on or off block statistics for devices.
52              
53             Don't give up if some of the modules doesn't run on your hardware! Tell me what's wrong
54             and I will try to solve it! You just have to make the first move and to send me a mail. :-)
55              
56             =head1 VIRTUAL MACHINES
57              
58             Note that if you try to install or run C under virtual machines
59             on guest systems that some statistics are not available, such as C, C
60             and C. The reason is that not all /proc data are passed to the guests.
61              
62             If the installation fails then try to force the installation with
63              
64             cpan> force install Sys::Statistics::Linux
65              
66             and notice which tests fails, because this statistics maybe not available on the virtual machine - sorry.
67              
68             =head1 DELTAS
69              
70             The statistics for C, C, C, C, C and C
71             are deltas, for this reason it's necessary to initialize the statistics before the data can be
72             prepared by C. These statistics can be initialized with the methods C, C and
73             C. For any option that is set to 1, the statistics will be initialized by the call of
74             C or C. The call of init() re-initialize all statistics that are set to 1 or 2.
75             By the call of C the initial statistics will be updated automatically. Please refer the
76             section L to get more information about the usage of C, C, C
77             and C.
78              
79             Another exigence is to sleep for a while - at least for one second - before the call of C
80             if you want to get useful statistics. The statistics for C, C, C,
81             C, C and C are no deltas. If you need only one of these information
82             you don't need to sleep before the call of C.
83              
84             The method C prepares all requested statistics and returns the statistics as a
85             L object. The inital statistics will be updated.
86              
87             =head1 MANUAL PROC(5)
88              
89             The Linux Programmer's Manual
90              
91             http://www.kernel.org/doc/man-pages/online/pages/man5/proc.5.html
92              
93             If you have questions or don't understand the sense of some statistics then take a look
94             into this awesome documentation.
95              
96             =head1 OPTIONS
97              
98             All options are identical with the package names of the distribution in lowercase. To activate
99             the gathering of statistics you have to set the options by the call of C or C.
100             In addition you can deactivate statistics with C.
101              
102             The options must be set with one of the following values:
103              
104             0 - deactivate statistics
105             1 - activate and init statistics
106             2 - activate statistics but don't init
107              
108             In addition it's possible to pass a hash reference with options.
109              
110             my $lxs = Sys::Statistics::Linux->new(
111             processes => {
112             init => 1,
113             pids => [ 1, 2, 3 ]
114             },
115             netstats => {
116             init => 1,
117             initfile => $file,
118             },
119             );
120              
121             Option C is useful if you want to store initial statistics on the filesystem.
122              
123             my $lxs = Sys::Statistics::Linux->new(
124             cpustats => {
125             init => 1,
126             initfile => '/tmp/cpustats.yml',
127             },
128             diskstats => {
129             init => 1,
130             initfile => '/tmp/diskstats.yml',
131             },
132             netstats => {
133             init => 1,
134             initfile => '/tmp/netstats.yml',
135             },
136             pgswstats => {
137             init => 1,
138             initfile => '/tmp/pgswstats.yml',
139             },
140             procstats => {
141             init => 1,
142             initfile => '/tmp/procstats.yml',
143             },
144             );
145              
146             Example:
147              
148             #!/usr/bin/perl
149             use strict;
150             use warnings;
151             use Sys::Statistics::Linux;
152              
153             my $lxs = Sys::Statistics::Linux->new(
154             pgswstats => {
155             init => 1,
156             initfile => '/tmp/pgswstats.yml'
157             }
158             );
159              
160             $lxs->get(); # without to sleep
161              
162             The initial statistics are stored to the temporary file:
163              
164             #> cat /tmp/pgswstats.yml
165             ---
166             pgfault: 397040955
167             pgmajfault: 4611
168             pgpgin: 21531693
169             pgpgout: 49511043
170             pswpin: 8
171             pswpout: 272
172             time: 1236783534.9328
173              
174             Every time you call the script the initial statistics are loaded/stored from/to the file.
175             This could be helpful if you doesn't run it as daemon and if you want to calculate the
176             average load of your system since the last call. Do you understand? I hope so :)
177              
178             To get more information about the statistics refer the different modules of the distribution.
179              
180             sysinfo - Collect system information with Sys::Statistics::Linux::SysInfo.
181             cpustats - Collect cpu statistics with Sys::Statistics::Linux::CpuStats.
182             procstats - Collect process statistics with Sys::Statistics::Linux::ProcStats.
183             memstats - Collect memory statistics with Sys::Statistics::Linux::MemStats.
184             pgswstats - Collect paging and swapping statistics with Sys::Statistics::Linux::PgSwStats.
185             netstats - Collect net statistics with Sys::Statistics::Linux::NetStats.
186             sockstats - Collect socket statistics with Sys::Statistics::Linux::SockStats.
187             diskstats - Collect disk statistics with Sys::Statistics::Linux::DiskStats.
188             diskusage - Collect the disk usage with Sys::Statistics::Linux::DiskUsage.
189             loadavg - Collect the load average with Sys::Statistics::Linux::LoadAVG.
190             filestats - Collect inode statistics with Sys::Statistics::Linux::FileStats.
191             processes - Collect process statistics with Sys::Statistics::Linux::Processes.
192              
193             =head1 METHODS
194              
195             =head2 new()
196              
197             Call C to create a new Sys::Statistics::Linux object. You can call C with options.
198             This options would be passed to the method C.
199              
200             Without options
201              
202             my $lxs = Sys::Statistics::Linux->new();
203              
204             Or with options
205              
206             my $lxs = Sys::Statistics::Linux->new( cpustats => 1 );
207              
208             Would do nothing
209              
210             my $lxs = Sys::Statistics::Linux->new( cpustats => 0 );
211              
212             It's possible to call C with a hash reference of options.
213              
214             my %options = (
215             cpustats => 1,
216             memstats => 1
217             );
218              
219             my $lxs = Sys::Statistics::Linux->new(\%options);
220              
221             =head2 set()
222              
223             Call C to activate or deactivate options.
224              
225             The following example would call C and initialize C
226             and delete the object of C.
227              
228             $lxs->set(
229             processes => 0, # deactivate this statistic
230             pgswstats => 1, # activate the statistic and calls new() and init() if necessary
231             netstats => 2, # activate the statistic and call new() if necessary but not init()
232             );
233              
234             It's possible to call C with a hash reference of options.
235              
236             my %options = (
237             cpustats => 2,
238             memstats => 2
239             );
240              
241             $lxs->set(\%options);
242              
243             =head2 get()
244              
245             Call C to get the collected statistics. C returns a L
246             object.
247              
248             my $lxs = Sys::Statistics::Linux->new(\%options);
249             sleep(1);
250             my $stat = $lxs->get();
251              
252             Or you can pass the time to sleep with the call of C.
253              
254             my $stat = $lxs->get($time_to_sleep);
255              
256             Now the statistcs are available with
257              
258             $stat->cpustats
259              
260             # or
261              
262             $stat->{cpustats}
263              
264             Take a look to the documentation of L for more information.
265              
266             =head2 init()
267              
268             The call of C initiate all activated statistics that are necessary for deltas. That could
269             be helpful if your script runs in a endless loop with a high sleep interval. Don't forget that if
270             you call C that the statistics are deltas since the last time they were initiated.
271              
272             The following example would calculate average statistics for 30 minutes:
273              
274             # initiate cpustats
275             my $lxs = Sys::Statistics::Linux->new( cpustats => 1 );
276              
277             while ( 1 ) {
278             sleep(1800);
279             my $stat = $lxs->get;
280             }
281              
282             If you just want a current snapshot of the system each 30 minutes and not the average
283             then the following example would be better for you:
284              
285             # do not initiate cpustats
286             my $lxs = Sys::Statistics::Linux->new( cpustats => 2 );
287              
288             while ( 1 ) {
289             $lxs->init; # init the statistics
290             my $stat = $lxs->get(1); # get the statistics
291             sleep(1800); # sleep until the next run
292             }
293              
294             If you want to write a simple command line utility that prints the current workload
295             to the screen then you can use something like this:
296              
297             my @order = qw(user system iowait idle nice irq softirq total);
298             printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n", 'time', @order;
299              
300             my $lxs = Sys::Statistics::Linux->new( cpustats => 1 );
301              
302             while ( 1 ){
303             my $cpu = $lxs->get(1)->cpustats;
304             my $time = $lxs->gettime;
305             printf "%-20s%8s%8s%8s%8s%8s%8s%8s%8s\n",
306             $time, @{$cpu->{cpu}}{@order};
307             }
308              
309             =head2 settime()
310              
311             Call C to define a POSIX formatted time stamp, generated with localtime().
312              
313             $lxs->settime('%Y/%m/%d %H:%M:%S');
314              
315             To get more information about the formats take a look at C of POSIX.pm
316             or the manpage C.
317              
318             =head2 gettime()
319              
320             C returns a POSIX formatted time stamp, @foo in list and $bar in scalar context.
321             If the time format isn't set then the default format "%Y-%m-%d %H:%M:%S" will be set
322             automatically. You can also set a time format with C.
323              
324             my $date_time = $lxs->gettime;
325              
326             Or
327              
328             my ($date, $time) = $lxs->gettime();
329              
330             Or
331              
332             my ($date, $time) = $lxs->gettime('%Y/%m/%d %H:%M:%S');
333              
334             =head1 EXAMPLES
335              
336             A very simple perl script could looks like this:
337              
338             use strict;
339             use warnings;
340             use Sys::Statistics::Linux;
341              
342             my $lxs = Sys::Statistics::Linux->new( cpustats => 1 );
343             sleep(1);
344             my $stat = $lxs->get;
345             my $cpu = $stat->cpustats->{cpu};
346              
347             print "Statistics for CpuStats (all)\n";
348             print " user $cpu->{user}\n";
349             print " nice $cpu->{nice}\n";
350             print " system $cpu->{system}\n";
351             print " idle $cpu->{idle}\n";
352             print " ioWait $cpu->{iowait}\n";
353             print " total $cpu->{total}\n";
354              
355             Set and get a time stamp:
356              
357             use strict;
358             use warnings;
359             use Sys::Statistics::Linux;
360              
361             my $lxs = Sys::Statistics::Linux->new();
362             $lxs->settime('%Y/%m/%d %H:%M:%S');
363             print $lxs->gettime, "\n";
364              
365             If you want to know how the data structure looks like you can use C to check it:
366              
367             use strict;
368             use warnings;
369             use Sys::Statistics::Linux;
370             use Data::Dumper;
371              
372             my $lxs = Sys::Statistics::Linux->new( cpustats => 1 );
373             sleep(1);
374             my $stat = $lxs->get;
375              
376             print Dumper($stat);
377              
378             How to get the top 5 processes with the highest cpu workload:
379              
380             use strict;
381             use warnings;
382             use Sys::Statistics::Linux;
383              
384             my $lxs = Sys::Statistics::Linux->new( processes => 1 );
385             sleep(1);
386             my $stat = $lxs->get;
387             my @top5 = $stat->pstop( ttime => 5 );
388              
389             =head1 BACKWARD COMPATIBILITY
390              
391             The old options and keys - CpuStats, NetStats, etc - are still available but deprecated!
392             It's not possible to access the statistics via L and it's
393             not possible to call C and C if you use the old options.
394              
395             You should use the new options and access the statistics over the accessors
396              
397             $stats->cpustats
398              
399             or directly with
400              
401             $stats->{cpustats}
402              
403             =head1 PREREQUISITES
404              
405             Carp
406             POSIX
407             Test::More
408             Time::HiRes
409             UNIVERSAL
410              
411             =head1 EXPORTS
412              
413             No exports.
414              
415             =head1 TODOS
416              
417             * Are there any wishs from your side? Send me a mail!
418              
419             =head1 REPORTING BUGS
420              
421             Please report all bugs to .
422              
423             =head1 AUTHOR
424              
425             Jonny Schulz .
426              
427             =head1 COPYRIGHT
428              
429             Copyright (C) 2006-2008 by Jonny Schulz. All rights reserved.
430              
431             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
432              
433             =cut
434              
435             package Sys::Statistics::Linux;
436             our $VERSION = '0.66';
437              
438 15     15   486092 use strict;
  15         43  
  15         756  
439 15     15   82 use warnings;
  15         35  
  15         500  
440 15     15   89 use Carp qw(croak);
  15         33  
  15         1367  
441 15     15   24470 use POSIX qw(strftime);
  15         168188  
  15         112  
442 15     15   47153 use UNIVERSAL;
  15         452  
  15         82  
443 15     15   12939 use Sys::Statistics::Linux::Compilation;
  15         70  
  15         234  
444              
445             sub new {
446 15     15 1 6026 my $class = shift;
447 15         88 my $self = bless { obj => { } }, $class;
448              
449 15         117 my @options = qw(
450             SysInfo CpuStats ProcStats
451             MemStats PgSwStats NetStats
452             SockStats DiskStats DiskUsage
453             LoadAVG FileStats Processes
454             );
455              
456 15         48 foreach my $opt (@options) {
457             # backward compatibility
458 180         466 $self->{opts}->{$opt} = 0;
459 180         306 $self->{maps}->{$opt} = $opt;
460             # new style
461 180         273 my $lcopt = lc($opt);
462 180         397 $self->{opts}->{$lcopt} = 0;
463 180         421 $self->{maps}->{$lcopt} = $opt;
464             }
465              
466 15 50       473 $self->set(@_) if @_;
467 15         102 return $self;
468             }
469              
470             sub set {
471 16     16 1 2680 my $self = shift;
472 16         50 my $class = ref $self;
473 16 50       125 my $args = ref($_[0]) eq 'HASH' ? shift : {@_};
474 16         49 my $opts = $self->{opts};
475 16         39 my $obj = $self->{obj};
476 16         42 my $maps = $self->{maps};
477 16         37 my $pids = ();
478              
479 16         69 foreach my $opt (keys %$args) {
480 19 50       95 if (!exists $opts->{$opt}) {
481 0         0 croak "$class: invalid option '$opt'";
482             }
483              
484 19 50       299 if (ref($args->{$opt})) {
    50          
485 0   0     0 $opts->{$opt} = delete $args->{$opt}->{init} || 1;
486             } elsif ($args->{$opt} !~ qr/^[012]\z/) {
487 0         0 croak "$class: invalid value for '$opt'";
488             } else {
489 19         54 $opts->{$opt} = $args->{$opt};
490             }
491              
492 19 50       233 if ($opts->{$opt}) {
    0          
493 19         80 my $package = $class.'::'.$maps->{$opt};
494              
495             # require module - require know which modules are loaded
496             # and doesn't load a module twice.
497 19         33 my $require = $package;
498 19         112 $require =~ s/::/\//g;
499 19         39 $require .= '.pm';
500 19         15249 require $require;
501              
502 19 100       180 if (!$obj->{$opt}) {
503 18 50       96 if (ref($args->{$opt})) {
504 0         0 $obj->{$opt} = $package->new(%{$args->{$opt}});
  0         0  
505             } else {
506 18         167 $obj->{$opt} = $package->new();
507             }
508             }
509              
510             # get initial statistics if the function init() exists
511             # and the option is set to 1
512 19 100 66     391 if ($opts->{$opt} == 1 && UNIVERSAL::can($package, 'init')) {
513 11         51 $obj->{$opt}->init();
514             }
515              
516             } elsif (exists $obj->{$opt}) {
517 0         0 delete $obj->{$opt};
518             }
519             }
520             }
521              
522             sub init {
523 0     0 1 0 my $self = shift;
524 0         0 my $class = ref $self;
525 0         0 my $maps = $self->{maps};
526              
527 0         0 foreach my $opt (keys %{$self->{opts}}) {
  0         0  
528 0 0 0     0 if ($self->{opts}->{$opt} > 0 && UNIVERSAL::can(ref($self->{obj}->{$opt}), 'init')) {
529 0         0 $self->{obj}->{$opt}->init();
530             }
531             }
532             }
533              
534             sub get {
535 15     15 1 9001636 my ($self, $time) = @_;
536 15 50       134 sleep $time if $time;
537 15         67 my %stat = ();
538              
539 15         42 foreach my $opt (keys %{$self->{opts}}) {
  15         256  
540 360 100       1124 if ($self->{opts}->{$opt}) {
541 18         187 $stat{$opt} = $self->{obj}->{$opt}->get();
542 18 100       132 if ($opt eq 'netstats') {
543 1         5 $stat{netinfo} = $self->{obj}->{$opt}->get_raw();
544             }
545             }
546             }
547              
548 15         485 return Sys::Statistics::Linux::Compilation->new(\%stat);
549             }
550              
551             sub settime {
552 0     0 1   my $self = shift;
553 0 0         my $format = @_ ? shift : '%Y-%m-%d %H:%M:%S';
554 0           $self->{timeformat} = $format;
555             }
556              
557             sub gettime {
558 0     0 1   my $self = shift;
559 0 0         $self->settime(@_) unless $self->{timeformat};
560 0           my $tm = strftime($self->{timeformat}, localtime);
561 0 0         return wantarray ? split /\s+/, $tm : $tm;
562             }
563              
564             1;