File Coverage

blib/lib/Monitoring/Availability/Logs.pm
Criterion Covered Total %
statement 126 131 96.1
branch 50 68 73.5
condition 12 14 85.7
subroutine 17 17 100.0
pod 3 3 100.0
total 208 233 89.2


line stmt bran cond sub pod time code
1             package Monitoring::Availability::Logs;
2              
3 19     19   2736 use 5.008;
  19         67  
  19         773  
4 19     19   109 use strict;
  19         37  
  19         829  
5 19     19   95 use warnings;
  19         38  
  19         956  
6 19     19   111 use Data::Dumper;
  19         32  
  19         1006  
7 19     19   97 use Carp;
  19         35  
  19         1451  
8 19     19   2491 use POSIX qw(strftime);
  19         17847  
  19         130  
9              
10             use constant {
11 19         36296 STATE_UP => 0,
12             STATE_DOWN => 1,
13             STATE_UNREACHABLE => 2,
14              
15             STATE_OK => 0,
16             STATE_WARNING => 1,
17             STATE_CRITICAL => 2,
18             STATE_UNKNOWN => 3,
19              
20             START_NORMAL => 1,
21             START_RESTART => 2,
22             STOP_NORMAL => 0,
23             STOP_ERROR => -1,
24 19     19   4148 };
  19         43  
25              
26             $Monitoring::Availability::Logs::host_states = {
27             'OK' => 0,
28             'UP' => 0,
29             'DOWN' => 1,
30             'UNREACHABLE' => 2,
31             'RECOVERY' => 0,
32             'PENDING' => 0,
33             };
34              
35             $Monitoring::Availability::Logs::service_states = {
36             'OK' => 0,
37             'WARNING' => 1,
38             'CRITICAL' => 2,
39             'UNKNOWN' => 3,
40             'RECOVERY' => 0,
41             'PENDING' => 0,
42             };
43              
44             =head1 NAME
45              
46             Monitoring::Availability::Logs - Load/Store/Access Logfiles
47              
48             =head1 DESCRIPTION
49              
50             Store for logfiles
51              
52             =head2 new ( [ARGS] )
53              
54             Creates an C object.
55              
56             =cut
57              
58             sub new {
59 21     21 1 1736 my $class = shift;
60 21         358 my(%options) = @_;
61              
62 21         132 my $self = {
63             'verbose' => 0, # enable verbose output
64             'logger' => undef, # logger object used for verbose output
65             'log_string' => undef, # logs from string
66             'log_livestatus' => undef, # logs from a livestatus query
67             'log_file' => undef, # logs from a file
68             'log_dir' => undef, # logs from a dir
69             };
70              
71 21         90 bless $self, $class;
72              
73 21         122 for my $opt_key (keys %options) {
74 57 50       197 if(exists $self->{$opt_key}) {
75 57         130 $self->{$opt_key} = $options{$opt_key};
76             }
77             else {
78 0         0 croak("unknown option: $opt_key");
79             }
80             }
81              
82             # create an empty log store
83 21         80 $self->{'logs'} = [];
84              
85             # which source do we use?
86 21 100       206 if(defined $self->{'log_string'}) {
87 19         96 $self->_store_logs_from_string($self->{'log_string'});
88             }
89 21 50       104 if(defined $self->{'log_file'}) {
90 0         0 $self->_store_logs_from_file($self->{'log_file'});
91             }
92 21 50       109 if(defined $self->{'log_dir'}) {
93 0         0 $self->_store_logs_from_dir($self->{'log_dir'});
94             }
95 21 50       95 if(defined $self->{'log_livestatus'}) {
96 0         0 $self->_store_logs_from_livestatus($self->{'log_livestatus'});
97             }
98              
99 21         97 return $self;
100             }
101              
102             ########################################
103              
104             =head1 METHODS
105              
106             =head2 get_logs
107              
108             get_logs()
109              
110             returns all read logs as array of hashrefs
111              
112             =cut
113              
114             sub get_logs {
115 19     19 1 37 my $self = shift;
116 19         61 return($self->{'logs'});
117             }
118              
119             ########################################
120              
121             =head2 parse_line
122              
123             parse_line($line)
124              
125             return parsed logfile line
126              
127             =cut
128              
129             sub parse_line {
130 256 50   256 1 751 return if substr($_[0], 0, 1, '') ne '[';
131 256         808 my $return = {
132             'time' => substr($_[0], 0, 10, '')
133             };
134 256         370 substr($_[0], 0, 2, '');
135              
136 256         1650 ($return->{'type'},$_[0]) = split(/:\ /mxo, $_[0], 2);
137 256 100       2156 if(!$_[0]) {
138             # extract starts/stops
139 87         199 &_set_from_type($return);
140 87         180 return $return;
141             }
142              
143             # extract more information from our options
144 169         446 &_set_from_options($return->{'type'}, $return, $_[0]);
145              
146 169         314 return $return;
147             }
148              
149             ########################################
150             # INTERNAL SUBS
151             ########################################
152             sub _store_logs_from_string {
153 46     46   50325 my $self = shift;
154 46         109 my $string = shift;
155 46 50       153 return unless defined $string;
156 46         276 for my $line (split/\n/mxo, $string) {
157 202         4717 my $data = &parse_line($line);
158 202 50       508 push @{$self->{'logs'}}, $data if defined $data;
  202         703  
159             }
160 46         143 return 1;
161             }
162              
163             ########################################
164             sub _store_logs_from_file {
165 2     2   5740 my $self = shift;
166 2         6 my $file = shift;
167 2 50       12 return unless defined $file;
168              
169 2 50       88 open(my $FH, '<', $file) or croak('cannot read file '.$file.': '.$!);
170 2         55 while(my $line = <$FH>) {
171 54         68 chomp($line);
172 54         96 my $data = &parse_line($line);
173 54 50       115 push @{$self->{'logs'}}, $data if defined $data;
  54         246  
174             }
175 2         22 close($FH);
176 2         16 return 1;
177             }
178              
179             ########################################
180             sub _store_logs_from_dir {
181 1     1   8172 my $self = shift;
182 1         4 my $dir = shift;
183              
184 1 50       6 return unless defined $dir;
185              
186 1 50       324 opendir(my $dh, $dir) or croak('cannot open directory '.$dir.': '.$!);
187 1         37 while(my $file = readdir($dh)) {
188 3 100       21 if($file =~ m/\.log$/mxo) {
189 1         8 $self->_store_logs_from_file($dir.'/'.$file);
190             }
191             }
192 1         26 closedir $dh;
193              
194 1         5 return 1;
195             }
196              
197             ########################################
198             sub _store_logs_from_livestatus {
199 1     1   299 my $self = shift;
200 1         2 my $log_array = shift;
201 1 50       4 return unless defined $log_array;
202 1         2 for my $entry (@{$log_array}) {
  1         3  
203 11         22 my $data = $self->_parse_livestatus_entry($entry);
204 11 50       27 push @{$self->{'logs'}}, $data if defined $data;
  11         28  
205             }
206 1         3 return 1;
207             }
208              
209             ########################################
210             sub _parse_livestatus_entry {
211 11     11   13 my($self, $entry) = @_;
212              
213 11   100     105 my $string = $entry->{'message'} || $entry->{'options'} || '';
214 11 100       24 if($string eq '') {
215             # extract starts/stops
216 3         8 &_set_from_type($entry, $string);
217 3         4 return $entry;
218             }
219              
220             # extract more information from our options
221 8 50       16 if($entry->{'message'}) {
222 0         0 return &parse_line($string);
223             } else {
224 8         16 &_set_from_options($entry->{'type'}, $entry, $string);
225             }
226              
227 8         13 return $entry;
228             }
229              
230             ########################################
231             sub _set_from_options {
232 177     177   327 my($type, $data, $string) = @_;
233              
234             # Service States
235 177 100 100     1987 if( $type eq 'SERVICE ALERT'
    100 66        
    100 100        
    100 66        
    100          
236             or $type eq 'CURRENT SERVICE STATE'
237             or $type eq 'INITIAL SERVICE STATE'
238             ) {
239 55         333 my @tmp = split(/;/mxo, $string,6); # regex is faster than strtok here
240 55         126 $data->{'host_name'} = $tmp[0];
241 55         90 $data->{'service_description'} = $tmp[1];
242 55         174 $data->{'state'} = $Monitoring::Availability::Logs::service_states->{$tmp[2]};
243 55 50       136 return unless defined $data->{'state'};
244 55 100       135 $data->{'hard'} = $tmp[3] eq 'HARD' ? 1 : 0;
245 55         137 $data->{'plugin_output'} = $tmp[5];
246             }
247              
248             # Host States
249             elsif( $type eq 'HOST ALERT'
250             or $type eq 'CURRENT HOST STATE'
251             or $type eq 'INITIAL HOST STATE'
252             ) {
253 19         85 my @tmp = split(/;/mxo, $string,5); # regex is faster than strtok here
254 19         51 $data->{'host_name'} = $tmp[0];
255 19         57 $data->{'state'} = $Monitoring::Availability::Logs::host_states->{$tmp[1]};
256 19 50       70 return unless defined $data->{'state'};
257 19 50       58 $data->{'hard'} = $tmp[2] eq 'HARD' ? 1 : 0;
258 19         52 $data->{'plugin_output'} = $tmp[4];
259             }
260              
261              
262             # Host Downtimes
263             elsif($type eq 'HOST DOWNTIME ALERT') {
264 9         53 my @tmp = split(/;/mxo, $string,3); # regex is faster than strtok here
265 9         21 $data->{'host_name'} = $tmp[0];
266 9 100       43 $data->{'start'} = $tmp[1] eq 'STARTED' ? 1 : 0;
267             }
268              
269             # Service Downtimes
270             elsif($type eq 'SERVICE DOWNTIME ALERT') {
271 8         43 my @tmp = split(/;/mxo, $string,4); # regex is faster than strtok here
272 8         20 $data->{'host_name'} = $tmp[0];
273 8         15 $data->{'service_description'} = $tmp[1];
274 8 100       31 $data->{'start'} = $tmp[2] eq 'STARTED' ? 1 : 0;
275             }
276              
277             # Timeperiod Transitions
278             # livestatus does not parse this correct, so we have to use regex
279             elsif($type =~ m/^TIMEPERIOD\ TRANSITION/mxo) {
280 6         28 my @tmp = split(/;/mxo, $string,3); # regex is faster than strtok here
281 6         13 $data->{'type'} = 'TIMEPERIOD TRANSITION';
282 6         13 $data->{'timeperiod'} = $tmp[0];
283 6         11 $data->{'from'} = $tmp[1];
284 6         16 $data->{'to'} = $tmp[2];
285             }
286              
287 177         299 return 1;
288             }
289              
290             ########################################
291             sub _set_from_type {
292 90     90   123 my($data) = @_;
293              
294             # program starts
295 90 100       590 if($data->{'type'} =~ m/\ starting\.\.\./mxo) {
    100          
    100          
    100          
296 42         92 $data->{'proc_start'} = START_NORMAL;
297             }
298             elsif($data->{'type'} =~ m/\ restarting\.\.\./mxo) {
299 5         11 $data->{'proc_start'} = START_RESTART;
300             }
301              
302             # program stops
303             elsif($data->{'type'} =~ m/shutting\ down\.\.\./mxo) {
304 25         57 $data->{'proc_start'} = STOP_NORMAL;
305             }
306             elsif($data->{'type'} =~ m/Bailing\ out/mxo) {
307 3         10 $data->{'proc_start'} = STOP_ERROR;
308             }
309              
310 90         149 return 1;
311             }
312              
313             ########################################
314              
315             1;
316              
317             =head1 AUTHOR
318              
319             Sven Nierlein, Enierlein@cpan.orgE
320              
321             =head1 COPYRIGHT AND LICENSE
322              
323             Copyright (C) 2010 by Sven Nierlein
324              
325             This library is free software; you can redistribute it and/or modify
326             it under the same terms as Perl itself.
327              
328             =cut
329              
330             __END__