#!/usr/pkg/bin/perl -w use Term::ANSIColor; use POSIX qw(strftime); use Getopt::Long; Getopt::Long::Configure("no_ignore_case"); $main_version="0.3.0"; # Copyright (C) 2000-2001, KARASZI 'RASZi' Istvan # # http://nrg.martos.bme.hu/release/colorize/ # # Released under GPL version 2 or higher, # http://www.gnu.org/copyleft/gpl.html # # Thanks: #linux.hu@ircnet, Andras, fu, Szilva (the Perl book ;) # HoFi for many ideas, tompos for the nrg's net connection # mhp for packing it into Debian GNU/Linux and ideas # Words, that we like or hate @good_words=("activ", "start", "ready", "online", "load", "ok", "register", "detected", "configured", "enable", "listen", "open", "complete", "attempt", "done", "check"); @bad_words=("warn", "restart", "exit", "stop", "end", "shutting", "down", "close", "unreach", "can't", "cannot", "skip", "deny", "disable", "ignored", "miss", "oops", "su", "not", "backdoor", "blocking", "ignoring", "unable", "readonly", "offline"); @error_words=("error", "crit", "invalid", "fail", "false"); @system_words=("ext2-fs", "reiserfs", "vfs", "iso", "isofs", "cslip", "ppp", "bsd", "linux", "ip", "tcp/ip", "mtrr", "pci", "isa", "scsi", "ide", "atapi", "bios", "cpu", "fpu"); # Item names @items=("default", "unknown", "date", "host", "mac", "pid", "pid-sqbr", "get", "post", "head", "put", "connect", "httpcodes", "ftpcodes", "gettime", "getsize", "debug", "error", "warning", "bad", "good", "repeat", "process", "dir", "prot", "service", "email", "size", "version", "address", "uri", "miss", "parent", "direct", "hit", "deny", "ident", "refresh", "swapfail", "ctype", "clength", "create", "swapin", "swapout", "release", "swapnum", "hdate", "lmdate", "expired", "incoming", "outgoing", "user", "proxyfunction", "numbers", "system", "subject"); # Default output $default_output="STDOUT"; # Default color $default_color="white"; # Config file places (order is important, we will override the previous config's values) @config_files=("/usr/pkg/etc/colorizerc", "$ENV{HOME}/.colorizerc"); # Set all item to a default value sub setdefaults { my($item); foreach $item(@items) { $color{$item}=$default_color; } } # Read configuration from a file sub readconfig { my($file, $item, $value, $i, $unknown); $file=shift; if (-e $file) { if (!open(CONF, "< $file")) { print(STDERR "Can't open config file '$file': $!!"."\n"); return(1); } else { while() { if (/^([^#]\S+)\s+([^#]+)/) { $item=$1; $value=$2; $unknown=1; # cut spaces from end $value =~ s/\s+$//; if ($value =~ /^'([^']+)'$/) { $value=$1; if (defined($color{$value})) { $color{$item}=$color{$value}; $unknown=0; } else { $unknown=2; } } else { foreach $i(@items) { if ($i eq $item) { $unknown=0; $color{$item}=$value; } } } print(STDERR "Undefined value in '$file': $item = $value."."\n") if ($unknown == 2); print(STDERR "Unknown item in '$file': $item = $value."."\n") if ($unknown == 1); } } close(CONF); return(0); } } else { return(2); } } # Print html's head with 'Cascading style sheets' sub printhtmlheader { my($ccolor, $pre, $col, $back); &printout(''); &printout(""."\n"); &printout("Generated by colorize.pl $main_version"."\n"); &printout(''."\n"); &printout(""."\n"); &printout(""."\n"); } sub endfont { &printout(""); $font=0; } sub printenter { if (!$html) { &printout("\n"); } else { &printout("
"."\n"); } &fileclose; &fileopen; } sub printcolor { my($ncolor, $getcolor); $getcolor=shift(@_); # Just a debug $ncolor=$color{"$getcolor"}; print(STDERR "BUG, unknown color: $getcolor!"."\n") if (!defined($ncolor) or !$ncolor); if (!$html) { eval("print(FILE color '$ncolor')"); } else { &endfont if ($font); &printout(''); $font=1; } } sub fileopen { # Try to open output if (($file eq "STDOUT") or ($file eq "STDERR")) { open(FILE, ">&".$file) or &error("Can't open output for write: ".$file.", $!!"."\n"); } else { open(FILE, ">> $file") or &error("Can't open output for write: ".$file.", $!!"."\n"); } } sub isexists { # Check, the file is exists or not # FIXME: if the file name is STDOUT or STDERR isexists return with 0 if (($file ne "STDOUT") and ($file ne "STDERR") and -e $file) { return(1); } else { return(0); } } sub fileclose { close(FILE); } sub printout { my $line=shift; print(FILE $line); } sub error { my $line=shift; print(STDERR $line); exit; } sub quit { print(STDERR "Thank you for using colorize..."."\n"); if (!$html) { &clearcol; } else { &endfont if ($font); # Removed, 'cause append to an html is bogus, when we close the body and html tag # &printout(""); } &fileclose; exit; } sub clearcol { print(FILE color 'reset') if (!$html); } sub resetcol { if (!$html) { print(FILE color 'reset'); # Just reset the color &printcolor("default"); # Set to default color } else { &printcolor("default"); } } sub space { my $spaces=shift; &resetcol(); # Reset the color, and write a space if (defined($spaces)) { &printout($spaces); } else { &printout(" "); } } sub printdate { my $line=shift; &printcolor("date"); &printout($line); &space(); } sub http_action { $_=shift(@_); if (/GET/) { &printcolor("get"); } elsif (/POST/) { &printcolor("post"); } elsif (/HEAD/) { &printcolor("head"); } elsif (/PUT/) { &printcolor("put"); } elsif (/CONNECT/) { &printcolor("connect"); } else { &printcolor("unknown"); } } sub proxy_hierarchy { $_=shift(@_); if (/^NO/) { &printcolor("warning"); } elsif (/DIRECT/) { &printcolor("direct"); } elsif (/PARENT/) { &printcolor("parent"); } elsif (/MISS/) { &printcolor("miss"); } else { &printcolor("unknown"); } } sub oops_message { $_=shift(@_); if (/^([^(]+)(\([^)]*\):)(.*)$/) { &printcolor("proxyfunction"); &printout($1); &message($2); &message($3); } else { &message($_); } } sub message { my(@line, $word, $post, $pre, $original_word, $j, $color); $_=shift(@_); if (/^((last message repeated \d+ times)|(-- MARK --))$/) { &printcolor("repeat"); &printout($_); } else { @line=split (/ /); for($j=0;$j <= $#line; $j++) { $word = $line[$j]; # Cut start and end characters (like ',", etc) if ($word =~ /^([`'".,!?:;(\[{<]+)([^`'".,!?:;(\[{<]\S+)$/) { $pre=$1; $word=$2; } if ($word =~ /^(\S+[^`'".,!?:;)\]}>])([`'".,!?:;)\]}>]+)$/) { $word=$1; $post=$2; } if (defined($pre)) { &resetcol(); &printout($pre); undef $pre; } $original_word=$word; $word=lc($word); $color=""; if ($word =~ /^(((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(([a-z0-9-_]+\.)+[a-z]{2,3})|(localhost)|(\w*::\w+)+)(:\d{1,5})?)$/) { $color="host"; } elsif ($word =~ /^([0-9a-f]{2}:){5}[0-9a-f]{2}$/i) { $color="mac"; } elsif ($word =~ /^\/(\S)+$/) { $color="dir"; } elsif ($word =~ /^[a-z0-9-_]+@([a-z0-9-_\.]+)+(\.[a-z]{2,3})?$/) { $color="email"; } elsif ($word =~ /^\w{2,}:\/\/(\S+\/?)+$/) { $color="uri"; } elsif ($word =~ /^\d+(\.\d+)?[k|M|G]{1}B?(ytes?)?$/i) { $color="size"; } elsif ($word =~ /^v?(\d+\.){1}((\d|[a-z])+\.)*(\d|[a-z])+$/) { $color="version"; } elsif ($word =~ /^0x(\d|[a-f])+$/) { $color="address"; } elsif (getservbyname($word, "")) { $color="service"; } elsif (getprotobyname($word)) { $color="prot"; } elsif (defined(getpwnam($word))) { $color="user"; } elsif ($word =~ /^-?\d+$/) { $color="numbers"; } else { for(my $i=0; $i <= $#good_words; $i++) { $color="good" if ($word =~ /^$good_words[$i]/); } for(my $i=0; $i <= $#bad_words; $i++) { $color="bad" if ($word =~ /^$bad_words[$i]/); } for(my $i=0; $i <= $#error_words; $i++) { $color="error" if ($word =~ /^$error_words[$i]/); } for(my $i=0; $i <= $#system_words; $i++) { $color="system" if ($word =~ /^$system_words[$i]/); } } if ($color) { &printcolor($color); } else { &printcolor("unknown"); } &printout($original_word); if (defined($post)) { &resetcol(); &printout($post); undef $post; } &space() if ($j != $#line); } } } ## Main program ## # Constants $html=""; $file=$default_output; $SIG{'TERM'}="quit"; $SIG{'KILL'}="quit"; GetOptions('file|f=s' => \$file, 'html|h' => \$html, 'help|?' => \$help, 'version|V' => \$version, 'rcfile|F=s' => \$rcfile, 'convert-date|C' => \$convert_date, 'remove-facility|r' => \$remove_facility); if ($version) { print("Version: ".$main_version."\n"); exit; } elsif ($help) { print("colorize.pl version ".$main_version."\n"); print("Usage: colorize.pl [OPTION]"."\n"); print(" -f, --file [file] print output to a [file]"."\n"); print(" -h, --html generate html output"."\n"); print(" -F, --rcfile [file] read config file from [file]"."\n"); print(" -C, --convert-date convert unix timestamp to readable format"."\n"); print(" -r, --remove-facility remove syslog-ng's facility from start of the lines"."\n"); print("\n"); print(" -?, --help display this help and exit"."\n"); print(" -V, --version output version information and exit"."\n"); exit; } elsif ($rcfile) { @config_files=($rcfile); undef($rcfile); } $fileexists=&isexists($file); # Open output &fileopen; # Set default colors &setdefaults; # Read all of config files my $noconfig=1; foreach my $config (@config_files) { $noconfig=0 if (!&readconfig ($config)); } undef(@config_files); print(STDERR "Can't read any configuration!"."\n") if ($noconfig); undef($noconfig); # Print out html header if file isn't exists and html switch is on &printhtmlheader() if (!$fileexists && $html); undef($fileexists); # main loop while() { if ($remove_facility) { # syslog-ng paste the facility level before log messages, we hate this, cut it off! s/^<[0-9]+>//; } if (/^(>From)\s(\S+)(\s+)(.*)$/) { # procmail log (from field) $header=$1; $mail=$2; $space=$3; $date=$4; &printcolor("default"); &printout($header); &space(); &printcolor("email"); &printout($mail); &space($space); &printdate($date); } elsif (/^\s(Subject:)\s(.*)$/) { # procmail log (subject field) $header=$1; $subject=$2; &space(); &printcolor("default"); &printout($header); &space(); &printcolor("subject"); &printout($subject); } elsif (/^(\s+)(Folder:)\s(\S+)(\s+)(\d+)$/) { # procmail log (folder field) $space1=$1; $header=$2; $folder=$3; $space2=$4; $size=$5; &space($space1); &printcolor("default"); &printout($header); &space(); &printcolor("dir"); &printout($folder); &space($space2); &printcolor("size"); &printout($size); } elsif (/^(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2})\s(.*)$/) { # Exim main log my($date, $msg, $action, $uniqn, $color); $date=$1; $msg=$2; if ($msg =~ /^(\S{16})\s(<=)\s(\S+.*)$/) { $color='incoming'; $uniqn=$1; $action=$2; $msg=$3; } elsif ($msg =~ /^(\S{16})\s(=>)\s(\S+.*)$/) { $color='outgoing'; $uniqn=$1; $action=$2; $msg=$3; } elsif ($msg =~ /^(\S{16})\s(==)\s(\S+.*)$/) { $color='error'; $uniqn=$1; $action=$2; $msg=$3; } elsif ($msg =~ /^(\S{16})\s(.*)$/) { $uniqn=$1; $msg=$2; } # If we know last message id, then we know what is the type of last message # (it's send or received or something else) if (defined($exim_last_uniqn) && defined($uniqn)) { if ($exim_last_uniqn eq $uniqn) { $color=$exim_last_color; } } $color="unknown" if (! defined($color)); &printdate($date); if (defined($uniqn)) { &printcolor($color); &printout($uniqn); $exim_last_uniqn=$uniqn; $exim_last_color=$color; &space(); } if (defined($action)) { &printout($action); &space(); } &message($msg); } elsif (/^(\d{9}\.\d{3})(\s+)(\d+)\s(\S+)\s(\w+)\/(\d{3})\s(\d+)\s(\w+)\s(\S+)\s(\S+)\s(\w+)\/(\S+)\s(\S*)$/) { # Proxy access.log my($date, $space, $elaps, $host, $action, $httpc, $gsize, $method, $uri, $ident, $hierar, $fhost, $content); $date=$1; # timestamp $space=$2; # space between time and elapsed time $elaps=$3; # elapsed time $host=$4; # client $action=$5; # action/code $httpc=$6; # http code $gsize=$7; # get size $method=$8; # method $uri=$9; # requested uri $ident=$10; # ident (disabled default, it's logged as "-") $hierar=$11; # hierarchy $fhost=$12; # from host $content=$13; # Content-type if ($convert_date) { my $mydate=strftime("%b %e %T", gmtime($date)); &printdate($mydate); } else { &printdate($date); } &printcolor("gettime"); &printout($elaps); &space(); &printcolor("host"); &printout($host); &space(); if ($action =~ /^ERR/) { &printcolor("error"); } elsif ($action =~ /MISS/) { &printcolor("miss"); } elsif ($action =~ /HIT/) { &printcolor("hit"); } elsif ($action =~ /DENIED/) { &printcolor("deny"); } elsif ($action =~ /REFRESH/) { &printcolor("refresh"); } elsif ($action =~ /SWAPFAIL/) { &printcolor("swapfail"); } elsif ($action =~ /NONE/) { &printcolor("debug"); } else { &printcolor("unknown"); } &printout($action); &resetcol(); &printout("/"); &printcolor("httpcodes"); &printout($httpc); &space(); &printcolor("getsize"); &printout($gsize); &space(); &http_action($method); &printout($method); &space(); &printcolor("uri"); &printout($uri); &space(); &printcolor("ident"); &printout($ident); &space(); &proxy_hierarchy($hierar); &printout($hierar); &resetcol(); &printout("/"); &printcolor("host"); &printout($fhost); &space(); &printcolor("ctype"); &printout($content); } elsif (/^(\d{9}\.\d{3})\s(\w+)\s(\w+)(\s+)\s(\d{3})\s(\d{9}|-1)(\s+)(\d{9}|-1)(\s+)(\d{9}|-1)\s(\S+)\s(-1|\d+)\/(\d+)\s(\w+)\s(\S+)$/) { # Proxy store.log my($date, $tag, $swapnum, $space1, $status, $hdate, $space2, $lmdate, $space3, $expire, $content, $length, $gsize, $method, $uri); $date=$1; # date of the event $tag=$2; # the event :) $swapnum=$3; # swap number $space1=$4; # space between swap number and http reply code $status=$5; # http reply code $hdate=$6; # Time from the HTTP Date reply header $space2=$7; # space between hdate and lmdate $lmdate=$8; # last modification date $space3=$9; # space between lmdate and expire $expire=$10; # expire date $content=$11; # Content-type $length=$12; # Content-Length $gsize=$13; # actually readed bytes $method=$14; # the request method (GET, POST, etc). $uri=$15; # requested uri if ($convert_date) { my $mydate=strftime("%b %e %T", gmtime($date)); &printdate($mydate); } else { &printdate($date); } if ($tag =~ /CREATE/) { &printcolor("create"); } elsif ($tag =~ /SWAPIN/) { &printcolor("swapin"); } elsif ($tag =~ /SWAPOUT/) { &printcolor("swapout"); } elsif ($tag =~ /RELEASE/) { &printcolor("release"); } else { &printcolor("unknown"); } &printout($tag); &space(); &printcolor("swapnum"); &printout($swapnum); &resetcol(); &printout($space1); &printcolor("httpcodes"); &printout($status); &space(); &printcolor("hdate"); &printout($hdate); &resetcol(); &printout($space2); &printcolor("lmdate"); &printout($lmdate); &resetcol(); &printout($space3); &printcolor("expired"); &printout($expire); &space(); &printcolor("ctype"); &printout($content); &space(); &printcolor("clength"); &printout($length); &resetcol(); &printout("/"); &printcolor("getsize"); &printout($gsize); &space(); &http_action($method); &printout($method); &space(); &printcolor("uri"); # I dunno, maybe it must be same color with method (please FIXME with ideas) &printout($uri); } elsif (/^(\d{4}\/\d{2}\/\d{2}\s(\d{2}:){2}\d{2}\|)\s(.*)$/) { # Proxy cache.log my($date, $msg); $date=$1; $msg=$3; if ($convert_date) { my $mydate=strftime("%b %e %T", gmtime($date)); &printdate($mydate); } else { &printdate($date); } &printcolor("debug"); # I think all cache.log message are debug level message &printout($msg); } elsif (/^(\w{3}\s\w{3}\s+\d+\s\d{2}:\d{2}:\d{2}\s\d{4})(\s+)(\[[^\]]+\])(.*)$/) { # oops.log $date=$1; $space=$2; $status=$3; $msg=$4; &printdate($date); &space($space); &message($status); &oops_message($msg); } elsif (/^(\S*)\s-\s(\S+)\s(\[\d{1,2}\/\S*\/\d{4}:\d{2}:\d{2}:\d{2}.{0,6}\])\s("([A-Z]{3,})\s[^"]+")\s(\d{3})\s(\d+|-)\s(.*)$/) { # httpd access.log format found my($host, $user, $date, $msg, $method, $full_action, $http_code, $gsize); $host=$1; $user=$2; $date=$3; $full_action=$4; $method=$5; $http_code=$6; $gsize=$7; $other=$8; &printcolor("host"); &printout($host); &space(); &printout("-"); &space(); &printcolor("user"); &printout($user); &space(); &printdate($date); &http_action($method); &printout($full_action); &space(); &printcolor("httpcodes"); &printout($http_code); &space(); &printcolor("getsize"); &printout($gsize); &space(); &printcolor("default"); &printout($other); } elsif (/^(\[\w{3}\s\w{3}\s{1,2}\d{1,2}\s\d{2}:\d{2}:\d{2}\s\d{4}\])\s(\[\w*\])\s(.*)$/) { # httpd error.log format found my($date, $level, $msg, $color); $date=$1; $level=$2; $msg=$3; &printdate($date); if ($level =~ /(debug|info|notice)/) { $color="debug"; } elsif ($level =~ /warn/) { $color="warning"; } elsif ($level =~ /(error|crit|alert|emerg)/) { $color="error"; } else { $color="unknown"; } &printcolor($color); &printout($level); &space(); &printcolor($color); &printout($msg); } elsif (/^(\S{3}\s\S{3}\s{1,2}\S{1,2}\s\d{2}:\d{2}:\d{2}\s\d{4})\s(\d+)\s(\S+)\s(\d+)\s(\S+)\s(.*)/) { # xferlog format found my($date, $time, $host, $gsiz, $gdir, $ftpc); $date=$1; $time=$2; $host=$3; $gsiz=$4; $gdir=$5; $ftpc=$6; &printdate($date); &printcolor("gettime"); &printout($time); &space(); &printcolor("host"); &printout($host); &space(); &printcolor("getsize"); &printout($gsiz); &space(); &printcolor("dir"); &printout($gdir); &space(); &printcolor("ftpcodes"); &printout($ftpc); } elsif (/^(\S*\s{1,2}\d{1,2}\s\d\d:\d\d:\d\d)\s(\S+)\s((\S+:?)\s(.*))$/) { # It's a system log my($date, $host, $send, $process, $msg, $pid); $date=$1; $host=$2; $send=$3; # Repeat color if ($send !~ /((last message repeated \d+ times)|(-- MARK --))/) { $process=$4; $msg=$5; } else { $msg=$send; } &printdate($date); &printcolor("host"); &printout($host); &space(); if (defined($process)) { if ($process =~ /\[(\d+)\]/) { $process=$`; $pid=$1; } &printcolor("process"); &printout($process); if (defined($pid)) { &printcolor("pid-sqbr"); &printout("["); &printcolor("pid"); &printout($pid); &printcolor("pid-sqbr"); &printout("]"); undef($pid); &resetcol(); &printcolor("process"); &printout(":"); } undef($process); &space(); } &message($msg); } else { # Print the unknown messages too chomp($_); &printcolor("unknown"); &printout($_); } &clearcol; &printenter; } &quit; # Say goodbye # vi: ts=4 ai