<?php
/*
* log_watcher_class.php
*
* @(#) $Id: log_watcher_class.php,v 1.5 2006/01/11 13:43:46 mlemos Exp $
*
*/
/*
{metadocument}<?xml version="1.0" encoding="ISO-8859-1"?>
<class>
<package>net.manuellemos.logwatcher</package>
<version>@(#) $Id: log_watcher_class.php,v 1.5 2006/01/11 13:43:46 mlemos Exp $</version>
<copyright>Copyright © (C) Manuel Lemos 2003</copyright>
<title>Log watcher</title>
<author>Manuel Lemos</author>
<authoraddress>hide@address.com</authoraddress>
<documentation>
<idiom>en</idiom>
<purpose>Watch a log file and send the newly added log lines to a given
e-mail address.</purpose>
<usage>Set the <variablelink>log_file_name</variablelink> variable with
the name of the log file that is meant to be watched.<paragraphbreak />
Call the function <functionlink>OpenLog</functionlink> to start
watching a new log file or to resume watching a previously analysed
log file.<paragraphbreak />
Call the function <functionlink>MailNews</functionlink> to send the
newly added log file lines to the e-mail address specified by the
variable <variablelink>watcher_email</variablelink>.<paragraphbreak />
Call the function <functionlink>CloseLog</functionlink> to close the
log file and store the position of the current end of log line in the
pointer file. The pointer file name is defined by the
<variablelink>log_file_name</variablelink> variable appending the
<variablelink>log_pointer_extension</variablelink> configuration
variable value.</usage>
</documentation>
{/metadocument}
*/
class log_watcher_class
{
/*
{metadocument}
<variable>
<name>error</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Store the message that is returned when an error
occurs.</purpose>
<usage>Check this variable to understand what happened when a call to
any of the class functions has failed.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $error='';
/*
{metadocument}
<variable>
<name>log_file_name</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Name of the log file to watch.</purpose>
<usage>Set this variable to the full or relative path of the log file
before it is watched by the class functions.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $log_file_name='';
/*
{metadocument}
<variable>
<name>log_pointer_extension</name>
<type>STRING</type>
<value>.ptr</value>
<documentation>
<purpose>Extension to append to the log file name to define the name
of the file where is stored a pointer that records the last log
line that was processed.</purpose>
<usage>Change this variable only of the default is not suitable.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $log_pointer_extension='.ptr';
/*
{metadocument}
<variable>
<name>watcher_email</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>E-mail address of the person that will be notified about the
new log lines.</purpose>
<usage>Set this variable with the address to where the report of the
new log lines is sent by the function
<functionlink>MailNews</functionlink>.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $watcher_email='';
/*
{metadocument}
<variable>
<name>watcher_name</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Name of the person that will be notified about the new log
lines.</purpose>
<usage>Setting this variable is optional.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $watcher_name='';
/*
{metadocument}
<variable>
<name>system_email</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>E-mail address of the system that will be sending the new
log lines report messages.</purpose>
<usage>Set this variable with the address that will be used by the
function <functionlink>MailNews</functionlink> as sender of the
messages. If possible, this address will also serve as bounce
address in case it is not possible to deliver the messages for
some reason.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $system_email='';
/*
{metadocument}
<variable>
<name>system_name</name>
<type>STRING</type>
<value>Log Watcher</value>
<documentation>
<purpose>Name of the person that will be notified about the new log
lines.</purpose>
<usage>Setting this variable is optional.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $system_name='Log Watcher';
/*
{metadocument}
<variable>
<name>mailer</name>
<type>STRING</type>
<value>http://www.phpclasses.org/logwatcher $Revision: 1.5 $</value>
<documentation>
<purpose>Identification of the mailer system that is specified in the
<tt>X-Mailer:</tt> headers as sender of the report e-mail
messages.</purpose>
<usage>Do not change this variable unless you have a reason to change
the identification of the mailer system that is sending the report
messages.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $mailer='http://www.phpclasses.org/logwatcher $Revision: 1.5 $';
/*
{metadocument}
<variable>
<name>mail</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Name of the global function that will be called to send
e-mail messages.</purpose>
<usage>If for some reason the PHP mail() function does not work in
your environment or you prefer to use a better alternative, set
this variable to the name of the alternative global function. It
should take the same parameters as the PHP mail()
function.<paragraphbreak />
If this variable is set to an empty string, the PHP mail() function
will be used.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $mail='';
/*
{metadocument}
<variable>
<name>report_message_template</name>
<type>STRING</type>
<value>Hello {WATCHER},{BREAK}{BREAK}The log file {LOG} has the following new lines:{BREAK}{BREAK}{LINES}{BREAK}{SYSTEM}{BREAK}</value>
<documentation>
<purpose>Template that defines how the new log lines report messages
are formatted.</purpose>
<usage>Change this variable only if it is necessary to change the
default report message template.<paragraphbreak />
The template is a string that has special marks that are replaced
by specific values. These are the currently supported marks:<paragraphbreak />
<tt>{BREAK}</tt> - message line break. Used this instead of
hardcoded line breaks as the class will replace these marks by the
appropriate line breaking character sequence according to the
system sending the messages.<paragraphbreak />
<tt>{LINES}</tt> - Text of the newly added lines.<paragraphbreak />
<tt>{LOG}</tt> - Name of the log file as defined by the
<variablelink>log_file_name</variablelink> variable.<paragraphbreak />
<tt>{SYSTEM}</tt> - Name of the system that is sending the report
messages as defined by the <variablelink>system_name</variablelink>
variable.<paragraphbreak />
<tt>{WATCHER}</tt> - Name of the person to send the report messages
as defined by the <variablelink>watcher_name</variablelink> variable.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $report_message_template='Hello {WATCHER},{BREAK}{BREAK}The log file {LOG} has the following new lines:{BREAK}{BREAK}{LINES}{BREAK}{SYSTEM}{BREAK}';
/* private variables */
var $log_file=0;
var $pointer_file=0;
var $log_size=0;
var $end_of_log_file=0;
var $stored_position=-1;
/*
{metadocument}
<function>
<name>OpenLog</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Open the log file and seek to the position of the line that
was read for the last time, or to the end of the file if the log is
being opened for the first time.</purpose>
<usage>Call this function first to start processing the log file.
Specify the name of the log file in the
<variablelink>log_file_name</variablelink> variable before calling
this function.</usage>
<returnvalue>Indicates whether the log file was successfully
opened.</returnvalue>
</documentation>
<do>
{/metadocument}
*/
Function OpenLog()
{
if(strlen($this->error))
return(0);
if(strlen($this->log_file_name)==0)
{
$this->error='it was not specified a valid log file name';
return(0);
}
if(strlen($this->log_pointer_extension)==0)
{
$this->error='it was not specified a valid log file pointer extension';
return(0);
}
if(!($this->log_file=@fopen($this->log_file_name,'r')))
{
$this->error='could not open the log file "'.$this->log_file_name.'"';
return(0);
}
$pointer_file_name=$this->log_file_name.$this->log_pointer_extension;
$reset_pointer=0;
$this->log_size=filesize($this->log_file_name);
if(file_exists($pointer_file_name)
&& filesize($pointer_file_name))
{
if(!($this->pointer_file=@fopen($pointer_file_name,'r+')))
$this->error='could not open the log pointer file "'.$pointer_file_name.'"';
else
{
if(!flock($this->pointer_file,2))
$this->error='could not lock the pointer file';
else
{
if(!($position=fgets($this->pointer_file)))
$this->error='could not read to the log pointer file';
else
{
if(fseek($this->pointer_file,0)!=0)
$this->error='could not seek to the beginning of the pointer file';
else
{
$position=trim($position);
if(!strcmp(strval(intval($position)),$position)
&& ($this->stored_position=intval($position))<$this->log_size)
{
if(intval($position)
&& fseek($this->log_file,intval($position))!=0)
$this->error='could not seek to the log last position';
}
else
$reset_pointer=1;
}
}
}
}
}
else
{
if(!($this->pointer_file=@fopen($pointer_file_name,'w')))
$this->error='could not open the log pointer file "'.$pointer_file_name.'"';
else
{
if(!flock($this->pointer_file,2))
$this->error='could not lock the pointer file';
else
$reset_pointer=1;
}
}
if(strlen($this->error)==0
&& $reset_pointer)
{
if(fseek($this->log_file,0,SEEK_END)!=0)
$this->error='could not seek to the end of the log file';
else
$this->end_of_log_file=1;
}
if(strlen($this->error))
{
if($this->pointer_file)
{
fclose($this->pointer_file);
$this->pointer_file=0;
}
fclose($this->log_file);
$this->log_file=0;
return(0);
}
return(1);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
/*
{metadocument}
<function>
<name>GetNewLogLine</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Retrieve the next new line of the log if it was not yet
reached the end of the file. This function is mostly meant to
assist the function <functionlink>MailNews</functionlink> or
others that may be implemented in the future.</purpose>
<usage>If you need to use this function, call it after successfully
calling the <functionlink>OpenLog</functionlink> function. Check
the value of the <argumentlink>
<function>GetNewLogLine</function>
<argument>end_of_log</argument>
</argumentlink> argument after calling this function to determine
if it was reached the end of the log file. If the end of the log
file was not yet reached, the <argumentlink>
<function>GetNewLogLine</function>
<argument>line</argument>
</argumentlink> argument will return the log line that was read,
already without any end of line characters.</usage>
<returnvalue>Indicates whether the next log line was retrieved
successfully or it was reached the end of the log file.</returnvalue>
</documentation>
<argument>
<name>line</name>
<type>STRING</type>
<out />
<documentation>
<purpose>Return the next line that to be read from the log file.</purpose>
</documentation>
</argument>
<argument>
<name>end_of_log</name>
<type>BOOLEAN</type>
<out />
<documentation>
<purpose>Return a flag value that indicates if it was reached the
end of the log file.</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function GetNewLogLine(&$line, &$end_of_log)
{
if(strlen($this->error))
return(0);
if($this->log_file==0)
{
$this->error='the log file is not opened';
return(0);
}
if(($end_of_log=$this->end_of_log_file))
return(1);
if(strcmp(GetType($position=ftell($this->log_file)), 'integer'))
$this->error='could not determine the current log file position';
else
{
if($position>=$this->log_size)
{
$this->log_size=$position;
$end_of_log=$this->end_of_log_file=1;
}
else
{
if(!($line=fgets($this->log_file)))
$this->error='could not read log file line';
else
$line=str_replace("\n",'',str_replace("\r",'',$line));
}
}
if(strlen($this->error))
{
fclose($this->pointer_file);
$this->pointer_file=0;
fclose($this->log_file);
$this->log_file=0;
return(0);
}
return(1);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
/*
{metadocument}
<function>
<name>MailNews</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Read all the new lines of the log file and send them in a
message to the address defined in the
<variablelink>watcher_email</variablelink> variable. If there are
no new lines in the log file, nothing happens.</purpose>
<usage>Call this function after successfully calling the
<functionlink>OpenLog</functionlink> function. Before calling this
function, specify the addresses of the watcher person that will
receive the report messages and the system that is sending them in
the variables <variablelink>watcher_email</variablelink> and
<variablelink>system_email</variablelink> respectively.</usage>
<returnvalue>Indicates whether the message was successfully sent
or there were no new log files to notify.</returnvalue>
</documentation>
<argument>
<name>subject</name>
<type>STRING</type>
<documentation>
<purpose>Subject of the message to be sent.</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function MailNews($subject)
{
if(strlen($this->watcher_email)==0)
{
$this->error='it was not specified a valid watcher e-mail address';
return(0);
}
if(strlen($this->system_email)==0)
{
$this->error='it was not specified a valid system e-mail address';
return(0);
}
$line_break=((defined('PHP_OS') && !strcmp(substr(PHP_OS,0,3),'WIN')) ? "\r\n" : "\n");
for($lines='';;)
{
if(!$this->GetNewLogLine($line, $end_of_log))
return(0);
if($end_of_log)
break;
$lines.=$line.$line_break;
}
if(strlen($lines)==0)
return(1);
$headers= 'Return-Path: <'.$this->system_email.'>'.$line_break.'From: '.(strlen($this->system_name) ? '"'.$this->system_name.'" ' : '').'<'.$this->system_email.'>'.$line_break;
if(strlen($this->mailer))
$headers.='X-Mailer: '.$this->mailer.$line_break;
$headers.='Mime-Version: 1.0'.$line_break.'Content-type: text/plain; charset=iso-8859-1'.$line_break.'Content-Transfer-Encoding: 8bit'.$line_break;
$message=wordwrap(str_replace('{LINES}',$lines,str_replace('{BREAK}',$line_break,str_replace('{SYSTEM}',$this->system_name,str_replace('{LOG}',$this->log_file_name,str_replace('{WATCHER}',$this->watcher_name,$this->report_message_template))))));
$to=(strlen($this->watcher_name) ? '"'.$this->watcher_name.'" ' : '').'<'.$this->watcher_email.'>';
$version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7");
if(strlen($this->mail)==0
&& defined('PHP_OS')
&& strcmp(substr(PHP_OS,0,3),'WIN')
&& $version[0]*1000000+$version[1]*1000+$version[2]>=4000005)
$success=mail($to,$subject,$message,$headers,"-f".$this->system_email);
else
{
$mail=(strlen($this->mail) ? $this->mail : 'mail');
$success=$mail($to,$subject,$message,$headers);
}
return($success);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
/*
{metadocument}
<function>
<name>CloseLog</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Close a previously opened log file and stores the end of log
file position in the log pointer file.</purpose>
<usage>Call this function after calling the function
<functionlink>MailNews</functionlink> successfully.</usage>
<returnvalue>Indicates whether the closing the log file and storing
the log pointer position was done successfully.</returnvalue>
</documentation>
<do>
{/metadocument}
*/
Function CloseLog()
{
if(strlen($this->error))
return(0);
if($this->log_file==0)
{
$this->error='the log file was not yet opened';
return(0);
}
if($this->stored_position!=$this->log_size
&& (!fputs($this->pointer_file,strval($this->log_size)."\n")
|| !fflush($this->pointer_file)))
$this->error='could not write to the pointer file';
fclose($this->pointer_file);
$this->pointer_file=0;
fclose($this->log_file);
$this->log_file=0;
return(strlen($this->error)==0);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
};
/*
{metadocument}
</class>
{/metadocument}
*/
?>