#!/usr/bin/perl
#-----------------------------------------------------------------------------
#
# xlog -- tools for handling xlog log files
#
#-----------------------------------------------------------------------------
#
# Copyright (C) 2001 Jochen Topf <jochen@remote.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
#
#-----------------------------------------------------------------------------
use strict;
use Getopt::Long;
use IO::File;
use Time::Local;
my $VERSION = "1.0";
my $session_grep = 0;
my %sessions;
my $subcommand = shift;
if ($subcommand eq "sgrep") {
$subcommand = "grep";
$session_grep = 1;
}
if ($subcommand eq "grep") {
my %opts = ();
GetOptions(\%opts, 'date=s', 'time=s', 'host=s', 'command=s', 'pid=i', 'sessionid=s', 'level=s@', 'id=s@', 'message=s@', 'x=s', 'v');
if (defined $opts{'date'}) {
if ($opts{'date'} =~ /^\d{8}$/) {
$opts{'startdate'} = $opts{'date'};
$opts{'enddate'} = $opts{'date'};
} elsif ($opts{'date'} =~ /^(\d{8})-$/) {
$opts{'startdate'} = $1;
$opts{'enddate'} = "99999999";
} elsif ($opts{'date'} =~ /^-(\d{8})$/) {
$opts{'startdate'} = "00000000";
$opts{'enddate'} = $1;
} elsif ($opts{'date'} =~ /^(\d{8})-(\d{8})$/) {
if ($1 < $2) {
$opts{'startdate'} = $1;
$opts{'enddate'} = $2;
} else {
$opts{'startdate'} = $2;
$opts{'enddate'} = $1;
}
} else {
print STDERR "xlog: illegal date: $opts{'date'}\n";
exit 1;
}
}
if (defined $opts{'time'}) {
if ($opts{'time'} =~ /^\d{6}$/) {
$opts{'starttime'} = $opts{'time'};
$opts{'endtime'} = $opts{'time'};
} elsif ($opts{'time'} =~ /^(\d{6})-$/) {
$opts{'starttime'} = $1;
$opts{'endtime'} = "235959";
} elsif ($opts{'time'} =~ /^-(\d{6})$/) {
$opts{'starttime'} = "000000";
$opts{'endtime'} = $1;
} elsif ($opts{'time'} =~ /^(\d{6})-(\d{6})$/) {
if ($1 lt $2) {
$opts{'starttime'} = $1;
$opts{'endtime'} = $2;
} else {
$opts{'starttime'} = $2;
$opts{'endtime'} = $1;
}
} else {
print STDERR "xlog: illegal time: $opts{'time'}\n";
exit 1;
}
}
if (defined $opts{'sessionid'} && $opts{'sessionid'} !~ /^(\d+\.\d+|-)$/) {
print STDERR "xlog: illegal session id: $opts{'sessionid'}\n";
exit 1;
}
if (defined $opts{'level'}) {
foreach (split(/,/, join(',', @{$opts{'level'}}))) {
if (! /^(DBG|INF|ERR|ADM|SOS)$/) {
print STDERR "xlog: illegal level: $_\n";
exit 1;
}
$opts{'Hlevel'}->{$_} = 1;
}
}
if (defined $opts{'id'}) {
foreach (split(/,/, join(',', @{$opts{'id'}}))) {
if (! /^[0-9a-fA-F]+$/) {
print STDERR "xlog: illegal message id: $_\n";
exit 1;
}
$opts{'Hid'}->{hex($_)} = 1;
}
}
if (defined $opts{'message'}) {
foreach (split(/,/, join(',', @{$opts{'message'}}))) {
if (! /^[a-z_]+$/) {
print STDERR "xlog: illegal message name: $_\n";
exit 1;
}
$opts{'Hmessage'}->{$_} = 1;
}
}
@ARGV = map { /\.(gz|Z)$/ ? "gzip -dc < $_ |" : $_ } @ARGV;
my @SAVEARGV = @ARGV;
xlog_grep(\%opts);
if ($session_grep) {
@ARGV = @SAVEARGV;
while (<>) {
my ($date, $time, $host, $command, $pid, $sessionid, $level, $id, $message, $text) = split(/ /, $_, 10);
print if ($sessions{$sessionid});
}
}
} elsif ($subcommand eq "join") {
xlog_join();
} elsif ($subcommand eq "stime") {
xlog_stime();
} elsif ($subcommand eq "stat") {
xlog_stat();
} elsif ($subcommand eq "cut") {
my %opts = ();
GetOptions(\%opts, 'date', 'time', 'host', 'command', 'pid', 'sessionid', 'level', 'id', 'message', 'x');
xlog_cut(\%opts);
} elsif ($subcommand eq "help") {
print <<"EOF";
Usage: xlog <subcommand> [OPTIONS] ...
Subcommands:
xlog grep print only lines matching certain criteria
xlog sgrep print all lines belonging to sessions matching certain criteria
xlog join join several logfiles sorting the lines by date/time
xlog stat display statistics on log file contents
xlog cut display only some columns of a log file
xlog version print version number
xlog help this help text
Options:
xlog [s]grep [-date DATE] [-time TIME] [-host HOST] [-command COMMAND]
[-pid PID] [-sessionid SESSIONID] [-level LEVEL] [-id ID]
[-message MESSAGE] [-x TEXT] <filename> ...
xlog cut [-date] [-time] [-host] [-command] [-pid] [-sessionid]
[-level] [-id] [-message] [-x]
All options can be abbreviated to the first letter. DATE and TIME can be
ranges like '20010425-20010426'. LEVEL, ID, and MESSAGE can be comma
separated lists. TEXT is a regular expression, all others are just strings.
EOF
exit 0;
} elsif ($subcommand eq "version") {
print "xlog $VERSION\n";
exit 0;
} else {
print STDERR "xlog: unknown or missing subcommand.\n";
exit 1;
}
#-----------------------------------------------------------------------------
#
# xlog_grep
#
#-----------------------------------------------------------------------------
sub xlog_grep {
my ($h) = @_;
while (<>) {
chomp;
my ($date, $time, $host, $command, $pid, $sessionid, $level, $id, $message, $text) = split(/ /, $_, 10);
if ((!defined $h->{'date'} || ($h->{'startdate'} le $date && $date le $h->{'enddate'})) &&
(!defined $h->{'time'} || ($h->{'starttime'} le $time && $time le $h->{'endtime'})) &&
(!defined $h->{'host'} || $h->{'host'} eq $host) &&
(!defined $h->{'command'} || $h->{'command'} eq $command) &&
(!defined $h->{'pid'} || $h->{'pid'} == $pid) &&
(!defined $h->{'sessionid'} || $h->{'sessionid'} eq $sessionid) &&
(!defined $h->{'level'} || $h->{'Hlevel'}->{$level}) &&
(!defined $h->{'id'} || $h->{'Hid'}->{hex($id)}) &&
(!defined $h->{'message'} || $h->{'Hmessage'}->{$message}) &&
(!defined $h->{'x'} || $text =~ /$h->{'x'}/)) {
if (! $h->{'v'}) {
if ($session_grep) {
$sessions{$sessionid} = 1 unless ($sessionid eq '-');
} else {
print "$_\n";
}
}
} else {
if ($h->{'v'}) {
if ($session_grep) {
$sessions{$sessionid} = 1 unless ($sessionid eq '-');
} else {
print "$_\n";
}
}
}
}
}
#-----------------------------------------------------------------------------
#
# xlog_stat
#
#-----------------------------------------------------------------------------
sub xlog_stat {
my %count_level;
my %count_id;
my %count_msg;
my $count;
while (<>) {
chomp;
my ($date, $time, $host, $command, $pid, $sessionid, $level, $id, $message, $text) = split(/ /, $_, 10);
$count_level{$level}++;
$count_id{"$message $id $level"}++;
$count_msg{"$message ---- $level"}++;
$count++;
}
printf("%40s: %10d (100%)\n", "all messages", $count);
print "------------------------------------------------------------------------------\n";
for my $lvl ("DBG", "INF", "ERR", "ADM", "SOS") {
printf("%40s: %10d (%3d%)\n", $lvl, $count_level{$lvl}, 100*$count_level{$lvl} / $count);
}
printf("%40s: %10d\n", "ERR/INF", $count_level{'INF'} > 0 ? $count_level{'ERR'} / $count_level{'INF'} : 0);
printf("%40s: %10d\n", "ADM/INF", $count_level{'INF'} > 0 ? $count_level{'ADM'} / $count_level{'INF'} : 0);
print "------------------------------------------------------------------------------\n";
for my $msg (sort { $count_msg{$b} <=> $count_msg{$a} } keys(%count_msg)) {
printf("%40s: %10d (%3d%)\n", $msg, $count_msg{$msg}, 100*$count_msg{$msg} / $count);
}
print "------------------------------------------------------------------------------\n";
for my $id (sort { $count_id{$b} <=> $count_id{$a} } keys(%count_id)) {
printf("%40s: %10d (%3d%)\n", $id, $count_id{$id}, 100*$count_id{$id} / $count);
}
}
#-----------------------------------------------------------------------------
#
# xlog_cut
#
#-----------------------------------------------------------------------------
sub xlog_cut {
my ($h) = @_;
while (<>) {
chomp;
my ($date, $time, $host, $command, $pid, $sessionid, $level, $id, $message, $text) = split(/ /, $_, 10);
my $out = "";
$out .= "$date " if ($h->{'date'});
$out .= "$time " if ($h->{'time'});
$out .= "$host " if ($h->{'host'});
$out .= "$command " if ($h->{'command'});
$out .= "$pid " if ($h->{'pid'});
$out .= "$sessionid " if ($h->{'sessionid'});
$out .= "$level " if ($h->{'level'});
$out .= "$id " if ($h->{'id'});
$out .= "$message " if ($h->{'message'});
$out .= "$text " if ($h->{'x'});
chop $out;
print "$out\n" if ($out);
}
}
#-----------------------------------------------------------------------------
#
# smallest
#
#-----------------------------------------------------------------------------
sub smallest {
my ($r) = @_;
my $s;
foreach my $file (keys(%$r)) {
if (! defined $s) {
$s = $file;
} else {
$s = $file if ($r->{$file} lt $r->{$s});
}
}
return $s;
}
#-----------------------------------------------------------------------------
#
# xlog_join
#
#-----------------------------------------------------------------------------
sub xlog_join {
my %fh;
my %line;
if (@ARGV < 1) {
print "xlog: need at least one files for 'join' command\n";
exit 1;
}
foreach my $file (@ARGV) {
$fh{$file} = new IO::File $file;
if (! defined $fh{$file}) {
print "xlog: open of file '$file' failed: $!\n";
exit 1;
}
$line{$file} = $fh{$file}->getline();
if (! defined $line{$file}) {
$fh{$file}->close();
delete $fh{$file};
}
}
while (%fh) {
my $file = smallest(\%line);
print "$line{$file}";
$line{$file} = $fh{$file}->getline();
if (! defined $line{$file}) {
$fh{$file}->close();
delete $fh{$file};
delete $line{$file};
}
}
}
#-----------------------------------------------------------------------------
#
# iso2ar
#
#-----------------------------------------------------------------------------
sub iso2ar {
my ($date, $time) = @_;
$date =~ /^(....)(..)(..)$/;
my ($year, $mon, $mday) = ($1, $2, $3);
$mon--;
$time =~ /^(..)(..)(..)$/;
my ($hours, $min, $sec) = ($1, $2, $3);
return ($sec, $min, $hours, $mday, $mon, $year);
}
#-----------------------------------------------------------------------------
#
# xlog_stime
#
#-----------------------------------------------------------------------------
sub xlog_stime {
my %sids;
while (<>) {
chomp;
my ($date, $time, $host, $command, $pid, $sessionid, $level, $id, $message, $text) = split(/ /, $_, 10);
next if ($sessionid eq "-");
if (defined $sids{$sessionid}) {
$sids{$sessionid} =~ s/:.*$//;
$sids{$sessionid} .= ":$date $time $message";
} else {
$sids{$sessionid} = "$date $time";
}
}
foreach (sort { substr($a, 15) cmp substr($b, 15) } keys(%sids)) {
my ($date1, $time1, $date2, $time2, $message) = split(/[ :]/, $sids{$_});
my $diff = timelocal(iso2ar($date2, $time2)) - timelocal(iso2ar($date1, $time1));
print "$_ $date1 $time1 $date2 $time2 $diff $message\n";
}
}
#-- THE END ------------------------------------------------------------------
syntax highlighted by Code2HTML, v. 0.9.1