Location: PHPKode > scripts > Log watcher > log-watcher/log_watcher_class.php
<?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}

*/

?>
Return current item: Log watcher