# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # FILE: LogMon.prl # USAGE: perl LogMon.prl (ipaddr) (filename) # A simple Perl program that will continuously tail a streaming log file # and send selected lines to a syslog receiver. # The user must specify an IP address as the first argument, followed # by the pathname of the log file to monitor. The user can also # configure certain match patterns, facilities, and severities. (See # configuration sections below.) # EXAMPLE: perl LogMon.prl 127.0.0.1 /var/logs/oracle.log # The above example continuously monitors the /var/logs/oracle.log file # and sends log messages to 127.0.0.1 when lines are added to the file. # If the file is deleted or truncated, the program detects this and # re-initializes (but does not send an alert.) Also, if the file does # not exist, the program waits for the file to be created. # This code has been placed in the public domain by: # CorreLog, Inc. # http://www.CorreLog.com # Rights to use and modify this program for any reason and purpose is # hereby granted to any and all parties. CorreLog, Inc. accepts # no liabilities and makes no claims about any use of this program, # or any adaptation of this program, whatsoever. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Facility configuration section. The user may configure the various # facilities assigned to messages. The left column contains a match # keyword, the right column contains an official facility number to # use if the keyword is matched. The first keyword matched by any # message will be used. $default_facility = 16; # Default facility is "local0". $i = 0; $f_match_keywd[$i] = "kernel" ; $f_use_facility [$i] = 0 ; $i++; $f_match_keywd[$i] = "user" ; $f_use_facility [$i] = 1 ; $i++; $f_match_keywd[$i] = "mail" ; $f_use_facility [$i] = 2 ; $i++; $f_match_keywd[$i] = "system" ; $f_use_facility [$i] = 3 ; $i++; $f_match_keywd[$i] = "security" ; $f_use_facility [$i] = 4 ; $i++; $f_match_keywd[$i] = "secure" ; $f_use_facility [$i] = 4 ; $i++; $f_match_keywd[$i] = "internal" ; $f_use_facility [$i] = 5 ; $i++; $f_match_keywd[$i] = "printer" ; $f_use_facility [$i] = 6 ; $i++; $f_match_keywd[$i] = "print" ; $f_use_facility [$i] = 6 ; $i++; $f_match_keywd[$i] = "news" ; $f_use_facility [$i] = 7 ; $i++; $f_match_keywd[$i] = "network" ; $f_use_facility [$i] = 8 ; $i++; $f_match_keywd[$i] = "lock" ; $f_use_facility [$i] = 9 ; $i++; $f_match_keywd[$i] = "auth" ; $f_use_facility [$i] = 10 ; $i++; $f_match_keywd[$i] = "ftp" ; $f_use_facility [$i] = 11 ; $i++; $f_match_keywd[$i] = "ntp" ; $f_use_facility [$i] = 12 ; $i++; $f_match_keywd[$i] = "audit" ; $f_use_facility [$i] = 13 ; $i++; $f_match_keywd[$i] = "alert" ; $f_use_facility [$i] = 14 ; $i++; $f_match_keywd[$i] = "clock" ; $f_use_facility [$i] = 15 ; $i++; $f_match_keywd[$i] = "cron" ; $f_use_facility [$i] = 15 ; $i++; $f_match_keywd[$i] = "local0" ; $f_use_facility [$i] = 16 ; $i++; $f_match_keywd[$i] = "local1" ; $f_use_facility [$i] = 17 ; $i++; $f_match_keywd[$i] = "local2" ; $f_use_facility [$i] = 18 ; $i++; $f_match_keywd[$i] = "local3" ; $f_use_facility [$i] = 19 ; $i++; $f_match_keywd[$i] = "local4" ; $f_use_facility [$i] = 20 ; $i++; $f_match_keywd[$i] = "local5" ; $f_use_facility [$i] = 21 ; $i++; $f_match_keywd[$i] = "local6" ; $f_use_facility [$i] = 22 ; $i++; $f_match_keywd[$i] = "local7" ; $f_use_facility [$i] = 23 ; $i++; # Append additional facilities to this list. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Severity configuration section. The user may configure the various # severities assigned to messages. The left column contains a match # keyword, the right column contains an official severity number, where # 0 is "emergency" and 7 is "debug." The highest severity matched by # the message is used. The special severity of "-1" corresponds to # "disabled", and prevents any sending of the message, useful for # filtering messages. In particular, setting the $default_severity to # a value of -1 will cause only those messages matched below to be # sent to the syslog receiver. $default_severity = 5; # Default severity is "notice". $i = 0; $s_match_keywd[$i] = "emergency" ; $s_use_severity [$i] = 0 ; $i++; $s_match_keywd[$i] = "danger" ; $s_use_severity [$i] = 0 ; $i++; $s_match_keywd[$i] = "hazard" ; $s_use_severity [$i] = 0 ; $i++; $s_match_keywd[$i] = "alert" ; $s_use_severity [$i] = 1 ; $i++; $s_match_keywd[$i] = "attention" ; $s_use_severity [$i] = 1 ; $i++; $s_match_keywd[$i] = "critical" ; $s_use_severity [$i] = 2 ; $i++; $s_match_keywd[$i] = "urgent" ; $s_use_severity [$i] = 2 ; $i++; $s_match_keywd[$i] = "significant" ; $s_use_severity [$i] = 2 ; $i++; $s_match_keywd[$i] = "severe" ; $s_use_severity [$i] = 2 ; $i++; $s_match_keywd[$i] = "error" ; $s_use_severity [$i] = 3 ; $i++; $s_match_keywd[$i] = "fail" ; $s_use_severity [$i] = 3 ; $i++; $s_match_keywd[$i] = "fault" ; $s_use_severity [$i] = 3 ; $i++; $s_match_keywd[$i] = "crash" ; $s_use_severity [$i] = 3 ; $i++; $s_match_keywd[$i] = "abnormal" ; $s_use_severity [$i] = 3 ; $i++; $s_match_keywd[$i] = "warning" ; $s_use_severity [$i] = 4 ; $i++; $s_match_keywd[$i] = "unexpect" ; $s_use_severity [$i] = 4 ; $i++; $s_match_keywd[$i] = "caution" ; $s_use_severity [$i] = 4 ; $i++; $s_match_keywd[$i] = "notice" ; $s_use_severity [$i] = 5 ; $i++; $s_match_keywd[$i] = "note" ; $s_use_severity [$i] = 5 ; $i++; $s_match_keywd[$i] = "success" ; $s_use_severity [$i] = 5 ; $i++; $s_match_keywd[$i] = "start" ; $s_use_severity [$i] = 5 ; $i++; $s_match_keywd[$i] = "info" ; $s_use_severity [$i] = 6 ; $i++; $s_match_keywd[$i] = "information" ; $s_use_severity [$i] = 6 ; $i++; $s_match_keywd[$i] = "debug" ; $s_use_severity [$i] = 7 ; $i++; $s_match_keywd[$i] = "ignore" ; $s_use_severity [$i] = 7 ; $i++; # Append additional severities to this list. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # End of configuration data. # No need to modify below this line. use Socket; $ipaddr = $ARGV[0]; $filename = $ARGV[1]; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Find the last newline before the end of file. initialize_offset(); # Get the current file size. $f_size = (stat ($filename))[7]; # Begin main loop. for (;;) { # See if file size has changed. $c_size = (stat ($filename))[7]; if ($c_size != $f_size) { # File changed size. $f_size = $c_size; get_and_process_new_lines (); } else { # Wait five seconds before checking again. sleep(5); } } # Not reached. exit; # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub get_and_process_new_lines { # Return $line, starting at $offset, and terminating with a '\n'. # Update the value of $offset accordingly. $line = ""; if (! open (F, "< " . $filename)) { # Could not open this file. initialize_offset(); return(0); } elsif (! seek(F, $offset, 0)) { close(F); initialize_offset(); return(0); } while (! eof(F) ) { $c = getc F; if ($c eq "\n") { # Record new offset and send any messages: $offset = tell(F); send_line_as_message(); $line = ""; } else { # Continue building this line. $line = "${line}$c"; } } # End of file found. close (F); return(0); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub send_line_as_message { if ($line eq "") { return(0); # Speedup. } # Determine the facility number to use. $facility = $default_facility; $size = @f_match_keywd; for ($i = 0; $i < $size; $i++) { if ($line =~ m/$f_match_keywd[$i]/i) { $facility = $f_use_facility[$i]; last; } } # Determine the severity number to use. Note that the lowest number # (i.e. highest severity) of any match is used. $severity = 9999; $size = @s_match_keywd; for ($i = 0; $i < $size; $i++) { if ($line =~ m/$s_match_keywd[$i]/i) { if ($s_use_severity[$i] < $severity) { $severity = $s_use_severity[$i]; } } } if ($severity >= 9999) { # No severity was found. Use default. $severity = $default_severity; } # Send the log message here. sendlog(); # Message was sent. return(0); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub initialize_offset { $offset = 0; if (! open (F, "< " . $filename)) { # File may not exist. return(0); } elsif (! seek(F, -5000, 2)) { # Less than 5000 characters in the file seek (F, 0, 0); } # Find the position of the last '\n' in the file. while (! eof(F) ) { $c = getc F; if ($c == "\n") { $offset = tell(F); } } # Value of $offset is set. close(F); return(0); } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sub sendlog { # Send the $line value to the specified IP address. if (! defined($proto)) { # First pass. Get these values. $proto = getprotobyname('udp') or die "ERROR: No UDP protocol available"; # This will fail if the address or port number is not specified. $n_addr = inet_aton($ipaddr) or die "ERROR: Improper IP address argument specified"; $s_addr = sockaddr_in(514, $n_addr); } if ($severity >= 8 || $severity < 0) { # Disabled. Bypass any sending of messages. return(0); } # Create a UDP socket, and a UDP connection: socket (S, AF_INET, SOCK_DGRAM, $proto ) or die "ERROR: Cannot create socket. Verify permissions."; # This will fail if the address or port number is invalid. connect (S, $s_addr ) or die "ERROR: Connect to IP address failed"; $priority = $facility << 3; $priority = $priority | $severity; print S "<$priority>$line"; close(S); return(0); } # End of File