Index: openacs.org-dev/packages/monitoring/tcl/watchdog-procs.tcl
===================================================================
RCS file: /usr/local/cvsroot/openacs.org-dev/packages/monitoring/tcl/watchdog-procs.tcl,v
diff -u -r1.1.1.1 -r1.1.1.2
--- openacs.org-dev/packages/monitoring/tcl/watchdog-procs.tcl	9 Jul 2002 17:35:09 -0000	1.1.1.1
+++ openacs.org-dev/packages/monitoring/tcl/watchdog-procs.tcl	8 Oct 2002 15:47:14 -0000	1.1.1.2
@@ -13,48 +13,76 @@
      and sent any error messages to ad_system_owner.
 
     @author
+    @author Andrew Piskorski (atp@piskorski.com)
     @creation-date Nov 28, 1999
-    @cvs-id watchdog-defs.tcl,v 3.9.2.4 2000/07/31 00:44:59 lars Exp
+    @cvs-id $Id$
 }
 
-ad_proc wd_errors {{num_minutes ""} {num_bytes "200000"}} "" {
 
+ad_proc wd_errors {{
+   -external_parser_p 0
+   -num_minutes ""
+   -num_bytes {1024000}
+}} {
+   TODO:  Docs...
+} {
+    set proc_name {wd_errors}
+
     set options ""
     if ![empty_string_p $num_minutes] {
-	validate_integer "Minutes" "$num_minutes"
+        validate_integer "Minutes" "$num_minutes"
         set options "-${num_minutes}m "
     } else {
-	validate_integer "Bytes" "$num_bytes"
+        validate_integer "Bytes" "$num_bytes"
         set options "-${num_bytes}b "
     }
 
-    set default_command "[acs_package_root_dir monitoring]/bin/aolserver-errors.pl"
-    set command [ad_parameter -package_id [monitoring_pkg_id] WatchDogParser monitoring $default_command]
+    # TODO: Rather than using external_parser_p flag, my want to do the
+    # external_parser_p stuff if the WatchDogParser parameter is empty
+    # string.  --atp@piskorski.com, 2002/04/08 14:25 EDT
 
-    if { ![file exists $command] } {
-	ns_log Notice "watchdog(wd_errors): Can't find WatchDogParser: $command doesn't exist" 
+    if { $external_parser_p } {
+        set default_command "[acs_package_root_dir monitoring]/bin/aolserver-errors.pl"
+        set command [ad_parameter -package_id [monitoring_pkg_id] WatchDogParser monitoring $default_command]
+
+        if { ![file exists $command] } {
+            ns_log Notice "Watchdog($proc_name): Can't find WatchDogParser: $command doesn't exist" 
+        } else {
+            # This has been changed from the previous version's concat
+            # because it did not work.  Some quick testing
+            # did not reveal an elegant solution, so we're going back
+            # to the old less-elegant-but-functional solution
+
+            if { [catch { set result [exec $command $options [ns_info log]] } err_msg] } {
+                global errorInfo
+                ns_log Error "Watchdog($proc_name): $errorInfo"
+                return ""
+            } else {
+                return $result
+            }
+            
+        }
+
     } else {
-	# This has been changed from the previous version's concat
-	# because it did not work.  Some quick testing
-	# did not reveal an elegant solution, so we're going back
-	# to the old less-elegant-but-functional solution
-	if { [catch { set result [exec $command $options [ns_info log]] } err_msg] } {
-	    ns_log error "Watchdog(wd_errors): $err_msg"
-	    return ""
-	} else {
-	    return $result
-	}
+        return [wd_aolserver_errors -num_minutes $num_minutes -num_bytes $num_bytes [ns_info log]]
     }
 }
 
+
 ad_proc wd_email_frequency {} "" {
-    # in minutes
-    return [ad_parameter -package_id [monitoring_pkg_id] WatchDogFrequency monitoring 300]
+    # Frequency is in minutes.  First checks value from ad_parameter,
+    # if no such parameter, uses value from AOLserver nsd.tcl config
+    # file, if no such parameter, uses default of 15 minutes:
+    # --atp@piskorski.com, 2002/04/08 12:49 EDT
+    
+    return [ad_parameter -package_id [monitoring_pkg_id] WatchDogFrequency monitoring \
+            [ns_config "ns/server/[ns_info server]/acs/monitoring" WatchDogFrequency 15]]
 }
 
 ad_proc wd_people_to_notify {} "" {
 
     set people_to_notify [ad_parameter_all_values_as_list -package_id [monitoring_pkg_id] PersontoNotify monitoring]
+    
     if [empty_string_p $people_to_notify] {
         return [ad_system_owner]
     } else {
@@ -63,18 +91,17 @@
 }
 
 ad_proc wd_mail_errors {} "" {
-
+    set proc_name {wd_mail_errors}
+    
     set num_minutes [wd_email_frequency]   
-
-    ns_log Notice "Looking for errors..."
-
-    set errors [wd_errors $num_minutes]
-
+    ns_log Notice "Watchdog($proc_name): Looking for errors..."
+    set errors [wd_errors -num_minutes $num_minutes]
+    
     if {[string length $errors] > 50} {
-        ns_log Notice "Errors found"
-	# Let's put the url to this server in the email message
-	# to make it crystal clear which server is having problems
-	set message "
+        ns_log Notice "Watchdog($proc_name): Errors found."
+        # Let's put the url to this server in the email message
+        # to make it crystal clear which server is having problems
+        set message "
 ([ad_url])
 
 $errors"
@@ -88,9 +115,147 @@
 } {
     set system_owner [ad_system_owner]
     foreach person [wd_people_to_notify] {
-	ns_sendmail $person $system_owner $subject $message
+        ns_sendmail $person $system_owner $subject $message
     }
 }
 
 
-
+ad_proc wd_aolserver_errors {{
+    -num_minutes {}
+    -num_bytes   {1024000}
+} log_file } {
+    Tcl version of packages/monitoring/bin/aolserver-errors.pl, to run
+    inside of AOLserver rather than forking a Perl script.
+    <p>
+    Saves its run-to-run state in $lastreadfile, so AOLserver must have
+    write permission to the directory where $log_file is located.
+    
+    @author David Walker (openacs@grax.com)
+    @author Andrew Piskorski (atp@piskorski.com)
+} {
+    
+    if { ![empty_string_p $num_minutes] } {
+        validate_integer {Minutes} $num_minutes
+    } elseif { [empty_string_p $num_bytes] } {
+        set num_bytes 0
+    } else {
+        validate_integer {Bytes} $num_bytes
+    }
+    
+    set lastreadfile "${log_file}.lastread"
+    
+    if {[file exists $lastreadfile]} {
+        source "${lastreadfile}"
+        
+        # That will set the lastread (which is bytes) and lastread_time
+        # (which is a string like 'Tue Apr 09 18:44:49 2002') variables.
+    }
+    set log_file_size [file size $log_file]
+    
+    if { ![info exists lastread] || $lastread > $log_file_size } {
+        set lastread 0
+        set lastread_time "First Run"
+        
+        # If the log has been rolled, presumably it will now be smaller
+        # in size than it was last time.  So in that case, always read
+        # from the beginning of the file.
+        #
+        # TODO: But, it isn't GUARANTEED that it will always be smaller.
+        # Is there any other way for us to detect if the log has been
+        # rolled or otherwise fooled with?  Save the first line from the
+        # log into our $lastreadfile, if the first line in the current
+        # log is different, then we know the log has been rolled.
+        # --atp@piskorski.com, 2002/04/09 13:13 EDT
+    }
+    
+    # TODO: num_minutes is currently being ignored.  Change that.
+    # --atp@piskorski.com, 2002/04/08 18:33 EDT
+    
+    # If the log grew by more than num_bytes since the last run, then
+    # we review only the LAST num_bytes of the log, where the "end" of
+    # the log is the point that WAS the end at the time we set
+    # log_file_size, above.  To always read everything, set num_bytes
+    # to 0:
+    
+    if { $num_bytes > 0 } {
+        set sizediff [expr {$log_file_size - $lastread}]
+        
+        if { $sizediff > $num_bytes } {
+            set lastread [expr {$log_file_size - $num_bytes}]
+            append output "Log file grew by [expr {round(100.0 * [expr {$sizediff / 1024.0 / 1024.0}]) / 100.0}] megabytes.\nReporting on the last $num_bytes bytes of log:\n"
+        } 
+    }
+    
+    set fh [open $log_file]
+    seek $fh $lastread
+    
+    set in_error_p 0
+    set in_following_notice_p 0
+    
+    append output "Errors since $lastread_time\n";
+    
+    # Note that in the while loop below, if we simply read till EOF we
+    # will have a race condition with the other threads writing to the
+    # log - bad!  A stern chase is a long chase, so if the log is
+    # growing like crazy because our server is, say, looping over and
+    # over on an error (yes, I've seen it happen...), by the time this
+    # thread catches up, we may easily end up trying to email 50+ MB of
+    # error messages.  We avoid this race condition by checking our
+    # position in the file with tell:
+    #
+    # --atp@piskorski.com, 2002/07/26 00:46 EDT
+    
+    while { ![eof $fh] && ([tell $fh] <= $log_file_size) } {
+        gets $fh thisline
+        
+        # Each line in the AOLserver error (server) log looks like this:
+        # [13/Jul/2002:00:43:29][2074.5][-sched-] Notice: Running scheduled proc wd_mail_errors...
+        
+        # Using non-greedy like this works: 
+        #   {^\[(.*?)\]\[(.*?)\]\[(.*?)\](.*)$}
+        # but let's use negated character classes instead:
+        
+        if { [regexp -expanded -- {
+            ^\[([^\]]*)\]
+            \[([^\]]*)\]
+            \[([^\]]*)\]
+            (.*)$
+        } $thisline xxx var1 var2 var3 var4] } {
+            set time $var1
+            set message [string trim $var4]
+            
+            if {[string first "Error:" $message] == 0} {
+                append output "\n$time\n    $message\n";
+                set in_error_p 1
+                set in_following_notice_p 0
+            } elseif {[string first "Notice:" $message] == 0} {
+                if { $in_error_p } {
+                    set in_following_notice_p 1
+                    append output "    $message\n"
+                } else {
+                    set in_following_notice_p 0
+                }
+                set in_error_p 0
+            } else {
+                set in_error_p 0
+                set in_following_notice_p 0
+            }
+        } else {
+            # The regexp didn't match, so whatever this line is, it's NOT
+            # the start of a normal AOLserver ns_log message.
+            
+            if { $in_error_p || $in_following_notice_p } {
+                append output "$thisline\n"
+            }
+        }
+    }
+    set file_read_size [tell $fh]
+    close $fh
+    
+    set fh [open $lastreadfile w]
+    puts $fh "set lastread \"${file_read_size}\""
+    puts $fh "set lastread_time \"[ns_fmttime [clock seconds]]\""
+    close $fh
+    
+    return $output
+}