Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/multiline logs #35

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 51 additions & 3 deletions plugins-scripts/Nagios/CheckLogfiles.pm
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ sub init {
$self->{warning} = $params->{warning} || 0;
$self->{critical} = $params->{critical} || 0;
$self->{matchlines} = { OK => [], WARNING => [], CRITICAL => [], UNKNOWN => [] };

$self->{multiline} = $params->{multiline} || 0;
$self->{multilinestartpattern} = $params->{multilinestartpattern} || "\n";

$self->init_macros;
$self->default_options({ prescript => 1, smartprescript => 0,
supersmartprescript => 0, postscript => 1, smartpostscript => 0,
Expand Down Expand Up @@ -1514,7 +1518,7 @@ sub check_pidfile {
$pidfile_status = 0;
} else {
$pidfile_status = 2;
open(KILL, "/bin/ps -o args -e|");
open(KILL, "/bin/ps -o pid,args -e|");
while (<KILL>) {
if (/^(\d+)\s+.*check_logfiles.*/) {
if ($1 == $pid) {
Expand Down Expand Up @@ -2013,6 +2017,10 @@ sub init {
$self->{likeavirgin} = 0;
$self->{linesread} = 0;
$self->{linenumber} = 0; # used for context

$self->{multiline} = $params->{multiline} || 0;
$self->{multilinestartpattern} = $params->{multilinestartpattern} || "\n";

$self->{perfdata} = "";
$self->{max_readsize} = 1024 * 1024 * 128;
# sysread can only read SSIZE_MAX bytes in one operation.
Expand Down Expand Up @@ -2898,7 +2906,45 @@ sub scan {
$self->trace("fake seek positioned at offset %u", $logfile->{offset});
}

while (my $line = $logfile->{fh}->getline()) {
# use the following negative look-ahead pattern to get multiline output chunks
# each chunk then represents a log message which can be processed
#
my $multilinePattern = $self->{multilinestartpattern} . "(.(?!" . $self->{multilinestartpattern} . "))+";

my $remainder = "";
my $line;

while ( ( $line = $remainder ) ne "" || ( $line = $logfile->{fh}->getline() ) ) {

# multiline parsing works like this:
# concat line after line in logfile until concatted lines do not match anymore
# (checked by negative look-ahead pattern described above)
# treat the result as one log message
#
if ( $self->{multiline} ) {

$remainder = "";

# read until the multiline-pattern does not match anymore
#
while ( ( $line =~ /^$multilinePattern$/s ) && ( my $nextLine = $logfile->{fh}->getline() ) ) {

# stop if lines + nextLine would not match multiline pattern anymore
#
if ( ( $line . $nextLine ) =~ /^$multilinePattern$/s ) {

$line .= $nextLine;

} else {

# keep this line for the next loop
#
$remainder = $nextLine;
last;
}
}
}

if ($self->{timedout}) {
$self->trace(sprintf "leaving the scan loop after %d lines",
$self->{linesread});
Expand All @@ -2913,7 +2959,9 @@ sub scan {
Encode::decode($self->{options}->{encoding}, $line));
# the input stream is somewhat binary, so chomp doesn't know
# it neads to remove \r\n on windows.
$line =~ s/$1/\n/g if $line =~ /(\r\n?|\n\r?)/;
if ( ! $self->{multiline} ) {
$line =~ s/$1/\n/g if $line =~ /(\r\n?|\n\r?)/;
}
}
chomp($line);
#
Expand Down
59 changes: 59 additions & 0 deletions t/110multiline.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/perl -w
#
# 110multiline.t
#
# Test multiline parsing
#

use strict;
use Test::More tests => 3;
use Cwd;
use lib "../plugins-scripts";
use Nagios::CheckLogfiles::Test;
use constant TESTDIR => ".";

my $configfile = <<EOCFG;
\$protocolsdir = "./var/tmp";
\$seekfilesdir = "./var/tmp";

\@searches = (
{
tag => 'multiline',
options => 'allyoucaneat',
multiline => 1,
multilinestartpattern => '\\d{4}\\-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} \\- ',
logfile => './data/multiline.log',
criticalpatterns => [ 'ERROR' ]
},
);

EOCFG
open CCC, ">./etc/multiline.cfg";
print CCC $configfile;
close CCC;

my $cl = Nagios::CheckLogfiles::Test->new({ cfgfile => "./etc/multiline.cfg" });
$cl->reset();
$cl->run();
diag($cl->has_result());
diag($cl->{exitmessage});

my ($protocolFile) = glob( "./var/tmp/multiline.protocol*" );
open( PROTOCOL, "<$protocolFile" ) || fail( "Could not open protocol file '$protocolFile': $!" );
my @content = <PROTOCOL>;
close( PROTOCOL );

my $expectedContent = qq|CRITICAL Errors in multiline.log (tag multiline)
2017-08-07 19:51:02 - ERROR
Test subject glitched through catcher
Cake reward will be reduced
2017-08-07 19:51:09 - ERROR
.....<bzzzt>.....>...\$\%\&...:!
<reboot initi.... cake...\%\%\&/(
2017-08-07 19:57:37 - ERROR - but only one line really
|;

is(join( "", @content ), $expectedContent );
like($cl->{exitmessage}, qr/CRITICAL - \(3 errors in multiline.protocol-.+\) - 2017-08-07 19:57:37 - ERROR - but only one line really .../ );
ok($cl->expect_result(0, 0, 3, 0, 2));

20 changes: 20 additions & 0 deletions t/data/multiline.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
2017-08-07 19:51:00 - INFO
Testchamber 16 initialized
Number of portals set to seven
2017-08-07 19:51:02 - ERROR
Test subject glitched through catcher
Cake reward will be reduced
2017-08-07 19:51:07 - INFO
Testchamber 17 initialized
Test subject entered with fully upgraded Portal Gun
2017-08-07 19:51:09 - ERROR
.....<bzzzt>.....>...$%&...:!
<reboot initi.... cake...%%&/(
2017-08-07 19:57:35 - WARNING
Test subject refused to euthenize Companion Cube
Waiting two more minutes
2017-08-07 19:57:35 - FATAL
Test subject reached sanctuary
Initiating defenses
Deploying turrets
2017-08-07 19:57:37 - ERROR - but only one line really