From 3628bfe141ef02299beb9514397cfc2c27a7dcb1 Mon Sep 17 00:00:00 2001
From: wei <>
Date: Wed, 4 Jan 2006 08:10:56 +0000
Subject: adding TEventLog using ezcLog from ezComponents.
---
framework/Log/EventLog/context.php | 167 ++++++
.../Log/EventLog/exceptions/file_exception.php | 44 ++
.../Log/EventLog/exceptions/writer_exception.php | 30 +
framework/Log/EventLog/interfaces/writer.php | 45 ++
framework/Log/EventLog/log.php | 644 +++++++++++++++++++++
framework/Log/EventLog/log_autoload.php | 25 +
framework/Log/EventLog/log_message.php | 169 ++++++
framework/Log/EventLog/map.php | 386 ++++++++++++
framework/Log/EventLog/structs/log_filter.php | 74 +++
framework/Log/EventLog/writers/writer_database.php | 216 +++++++
framework/Log/EventLog/writers/writer_file.php | 273 +++++++++
.../Log/EventLog/writers/writer_unix_file.php | 100 ++++
12 files changed, 2173 insertions(+)
create mode 100644 framework/Log/EventLog/context.php
create mode 100644 framework/Log/EventLog/exceptions/file_exception.php
create mode 100644 framework/Log/EventLog/exceptions/writer_exception.php
create mode 100644 framework/Log/EventLog/interfaces/writer.php
create mode 100644 framework/Log/EventLog/log.php
create mode 100644 framework/Log/EventLog/log_autoload.php
create mode 100644 framework/Log/EventLog/log_message.php
create mode 100644 framework/Log/EventLog/map.php
create mode 100644 framework/Log/EventLog/structs/log_filter.php
create mode 100644 framework/Log/EventLog/writers/writer_database.php
create mode 100644 framework/Log/EventLog/writers/writer_file.php
create mode 100644 framework/Log/EventLog/writers/writer_unix_file.php
(limited to 'framework/Log/EventLog')
diff --git a/framework/Log/EventLog/context.php b/framework/Log/EventLog/context.php
new file mode 100644
index 00000000..518d411e
--- /dev/null
+++ b/framework/Log/EventLog/context.php
@@ -0,0 +1,167 @@
+array)
+ */
+ protected $severityMap;
+
+ /**
+ * Stores the contexts for the sources.
+ *
+ * @var array(string=>array)
+ */
+ protected $sourceMap;
+
+
+ /**
+ * Resets this object to its initial state by removing all context mappings.
+ *
+ * @return void
+ */
+ public function reset()
+ {
+ unset( $this->severityMap );
+ unset( $this->sourceMap );
+ }
+
+ /**
+ * Set the context $context for the sevirities specified by $severityMask.
+ *
+ * If the severity already exist, the value will update the old value with
+ * the new one.
+ *
+ * $context is of the format array('key'=>'value').
+ *
+ * @param int $severityMask
+ * Bitmask that specifies all the event types that share the given
+ * context.
+ * @param array(string=>string) $context
+ * @return void
+ */
+ public function setSeverityContext( $severityMask, $context )
+ {
+ // For all the matching bits, add the context to the array.
+ $input = 1;
+ while ( $input <= $severityMask )
+ {
+ if ( $severityMask & $input )
+ {
+ if ( !isset( $this->severityMap[$input] ) )
+ {
+ $this->severityMap[$input] = array();
+ }
+
+ $this->severityMap[$input] = array_merge( (array) $this->severityMap[$input], (array) $context );
+ }
+
+ $input <<= 1;
+ }
+ }
+
+ /**
+ * Remove the contexts for the severities given by $severityMask.
+ *
+ * $severityMask is a bitmask that specifies all the event types that should remove
+ * their context.
+ *
+ * @param int $severityMask
+ * @return void
+ */
+ public function unsetSeverityContext( $severityMask )
+ {
+ // For all the matching bits, remove the context.
+ $input = 1;
+ while ( $input <= $severityMask )
+ {
+ if ( $severityMask & $input )
+ {
+ unset( $this->severityMap[$input] );
+ }
+
+ $input <<= 1;
+ }
+ }
+
+ /**
+ * Set the context $context for each eventSource specified by $eventSources.
+ *
+ * If a certain key from the given context does already exist, the
+ * new value will replace the value stored in the context itself. (Value is
+ * updated).
+ *
+ * @param array(string) $eventSources
+ * @param array(string=>string) $context
+ * Specifies the keys and values that should be stored into this
+ * context object.
+ * @return void
+ */
+ public function setSourceContext($eventSources, $context )
+ {
+ foreach ( $eventSources as $eventSource )
+ {
+ if ( !isset( $this->sourceMap[$eventSource] ) )
+ {
+ $this->sourceMap[$eventSource] = array();
+ }
+
+ $this->sourceMap[$eventSource] = array_merge( (array) $this->sourceMap[$eventSource], (array) $context );
+ }
+ }
+
+ /**
+ * Remove the contexts for the given $eventSources.
+ *
+ * @param array(string) $eventSources
+ * @return void
+ */
+ public function unsetSourceContext($eventSources )
+ {
+ foreach ( $eventSources as $eventSource )
+ {
+ unset( $this->sourceMap[$eventSource] );
+ }
+ }
+
+ /**
+ * Returns the complete context for the event type $eventType and event source $eventSource.
+ *
+ * If there is no context available this method will return an empty array.
+ *
+ *
+ * @param int $eventType The integer that specifies the event type.
+ * The range of this integer is 2^(N+):
+ * ( 1, 2, 4, 8, ... )
+ * @param string $eventSource
+ * @return array
+ */
+ public function getContext( $eventType, $eventSource )
+ {
+ $a = isset( $this->severityMap[$eventType] ) ? $this->severityMap[$eventType] : array();
+ $b = isset( $this->sourceMap[$eventSource] ) ? $this->sourceMap[$eventSource] : array();
+ return array_merge( $a, $b );
+ //return array_merge( (array) $this->severityMap[$eventType], (array) $this->sourceMap[$eventSource] );
+ }
+
+}
+?>
diff --git a/framework/Log/EventLog/exceptions/file_exception.php b/framework/Log/EventLog/exceptions/file_exception.php
new file mode 100644
index 00000000..03f3ebb9
--- /dev/null
+++ b/framework/Log/EventLog/exceptions/file_exception.php
@@ -0,0 +1,44 @@
+
diff --git a/framework/Log/EventLog/exceptions/writer_exception.php b/framework/Log/EventLog/exceptions/writer_exception.php
new file mode 100644
index 00000000..ae82bf07
--- /dev/null
+++ b/framework/Log/EventLog/exceptions/writer_exception.php
@@ -0,0 +1,30 @@
+
diff --git a/framework/Log/EventLog/interfaces/writer.php b/framework/Log/EventLog/interfaces/writer.php
new file mode 100644
index 00000000..82481e36
--- /dev/null
+++ b/framework/Log/EventLog/interfaces/writer.php
@@ -0,0 +1,45 @@
+string) $optional
+ * @return void
+ */
+ public function writeLogMessage( $message, $severity, $source, $category, $optional = array() );
+}
+?>
diff --git a/framework/Log/EventLog/log.php b/framework/Log/EventLog/log.php
new file mode 100644
index 00000000..a99569dd
--- /dev/null
+++ b/framework/Log/EventLog/log.php
@@ -0,0 +1,644 @@
+
+ * $filter = new ezcLogFilter();
+ * $filter->severity = ezcLog::INFO | ezcLog::NOTICE | ezcLog::WARNING | ezcLog::ERROR | ezcLog::FATAL;
+ *
+ * $log = ezcLog::getInstance();
+ * $log->map( $filter, new ezcLogWriterUnixFile( "/tmp/logs/", "error.log" ) );
+ *
+ *
+ * The log messages with the severity: INFO, NOTICE, WARNING, ERROR, and FATAL will
+ * be written to the file: "/tmp/logs/error.log". See {@link ezcLogWriterUnixFile} for
+ * the description of the file format.
+ *
+ * The following example will write the audit trails to the database:
+ *
+ * $filter = new ezcLogFilter();
+ * $filter->severity = ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT;
+ *
+ * $log = ezcLog::getInstance();
+ * $log->map( $filter, new ezcLogWriterDatabase( "audits" ) );
+ *
+ *
+ * The audit trails will be stored in the table "audits". See {@link ezcLogWriterDatabase}
+ * for creating the appropriate tables and setting up the database.
+ *
+ * It is also possible to exclude messages from going to a specific writer.
+ * This is done via the {@link unmap()} method. The following example shows
+ * how all messages, except for the DEBUG severity, are written to a file:
+ *
+ * $fileWriter = ezcLogWriterUnixFile( "/tmp/logs/", "all.log" );
+ * $filter = new ezcLogFilter();
+ * $ezcLog::getInstance()->map( $filter, $fileWriter ); // All severities.
+ *
+ * $filter->severity = ezcLog::DEBUG;
+ * ezcLog::getInstance()->unmap( $filter, $fileWriter ); // Remove DEBUG severity.
+ *
+ *
+ * Use the {@link log()} method to log messages at the specified writers. This
+ * method expects a:
+ * - Message, contains a single log message.
+ * - Severity, indicates the level of importance.
+ * - Extra attributes (optional).
+ *
+ * Although the interpretation of the severity levels are up to the programmer,
+ * the most common interpretations are:
+ * - DEBUG: Records information about the progress in the program and references
+ * source code functions. Knowledge of the source code is needed to interpret
+ * this log message.
+ * - INFO: Informative logging at a detailed level. This logging method produces a
+ * high level of logging, which is unmanageable on a production environment.
+ * Usually INFO logging is only enabled to help by analysing a problem.
+ * - NOTICE: Informative logging at a lower detail level than INFO logging.
+ * Only major stages are recorded and is useful to monitor a low volume system.
+ * - WARNING: Something unexpected happened, but did not cause any loss of service.
+ * - ERROR: An error occured, which may cause partial loss of service. Usually the
+ * system can recover.
+ * - FATAL: An serious error occured and the system is unlikely to recover.
+ * - SUCCESS_AUDIT: Informative logging about a successful completion of work by
+ * a module completed. Useful to trace system changes directly or indirectly
+ * done by a user.
+ * - FAILED_AUDIT: Informative logging about an action from a module
+ * with a negative result. A failed login will most likely added to this severity.
+ *
+ * The next example logs a fatal error and has no extra attributes:
+ *
+ * ezcLog::getInstance()->log( "Cannot open ini file: <$file>", ezcLog::FATAL );
+ *
+ *
+ * The log message will get by default the category and source: "default". The
+ * default values can be modified by changing, respectively, the properties:
+ * category and source. Their use is as follows:
+ * - Source, definition of the global location where the log message comes from.
+ * Some examples are: module, source file, extension, etc. The source depends
+ * also on the severity of the message. For DEBUG messages is the source file
+ * more important whereas for a FATAL error the module is sufficient.
+ * - Category, definition of the message group. Again the category is related to
+ * the severity. The non audit trails can group the log messages like: Database
+ * (or even the database types), Templates, etc. For audit trails it makes
+ * much sense to categorize the actions. For example: security, modified content,
+ * published content, shop, etc.
+ *
+ * An example of a Payment checker is as follows:
+ *
+ * // The start of the Payment module.
+ * $log = ezcLog::getInstance();
+ * $log->source = "Payment checker"; // Change the default source.
+ *
+ * $log->log( "Checking the received amount", ezcLog::INFO, array( "shop" ) );
+ *
+ * if( !$eZPay->receivedAmount() != $requiredAmount )
+ * {
+ * $log->log( "Received amount: <".$eZPay->receivedAmount()."> expected: <$requiredAmount>.",
+ * ezcLog::DEBUG,
+ * array( "category" => "shop", "file" => __FILE__, "line" => __LINE )
+ * );
+ *
+ * $log->log( "Insufficient amount.",
+ * ezcLog::FAILED_AUDIT,
+ * array( "UserName" => getCurrentUser(), category => "Payment" )
+ * )
+ *
+ * $log->log( "Rollback amount not implemented, cannot recover, ezcLog::FATAL );
+ * exit();
+ * }
+ *
+ *
+ * Sometimes information repeats for specific severities or categories. For example that
+ * for the audit trails an username is required. Convenience methods like:
+ * {@link setSeverityAttributes()} and {@link setSourceAttributes()} exist to append
+ * information automatically to the log message.
+ *
+ * The ezcLog class provides a {@link trigger_error()} log handler: {@link ezcLog::LogHandler()}.
+ * Using the trigger_error method makes your code less Log package dependent and
+ * produces less overhead when logging is disabled.
+ *
+ * See the {@link ezcLog::LogHandler()} method for more information about how to set up the
+ * trigger_error functionality.
+ *
+ * See the {@link ezcDebug} package for more detailed information about writing DEBUG
+ * messages.
+ *
+ * @package EventLog
+ * @version //autogentag//
+ */
+class ezcLog
+{
+ /**
+ * Debug severity constant.
+ */
+ const DEBUG = 1;
+
+ /**
+ * Success audit severity constant.
+ */
+ const SUCCESS_AUDIT = 2;
+
+ /**
+ * Failed audit severity constant.
+ */
+ const FAILED_AUDIT = 4;
+
+ /**
+ * Info severity constant.
+ */
+ const INFO = 8;
+
+ /**
+ * Notice severity constant.
+ */
+ const NOTICE = 16;
+
+ /**
+ * Warning severity constant.
+ */
+ const WARNING = 32;
+
+ /**
+ * Error severity constant.
+ */
+ const ERROR = 64;
+
+ /**
+ * Fatal severity constant.
+ */
+ const FATAL = 128;
+
+ /**
+ * Holds the properties of this class.
+ *
+ * @var array(string=>mixed)
+ */
+ private $properties = array();
+
+ /**
+ * Contains the logic of mapping an incoming log message to the writer.
+ *
+ * @var ezcLogMap
+ */
+ protected $writers;
+
+ /**
+ * Stores the attributes from the eventTypes and eventSources.
+ *
+ * $var ezcLogContext
+ */
+ protected $context;
+
+ /**
+ * Stores the instance of this class.
+ *
+ * @var ezcLog
+ */
+ private static $instance = null;
+
+ /**
+ * Stores the setting whether writer exceptions should be thrown.
+ *
+ * @var bool
+ */
+ private $throwWriterExceptions = true;
+
+ /**
+ * Constructs an empty ezcLog instance.
+ *
+ * This constructor is private as this class should be used as a
+ * singleton. Use the getInstance() method instead to get an ezcLog instance.
+ */
+ private function __construct()
+ {
+ $this->reset();
+ }
+
+ /**
+ * Returns the instance of the class.
+ *
+ * @return ezcLog
+ */
+ public static function getInstance()
+ {
+ if ( is_null( self::$instance ) )
+ {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Sets the property $name to $value.
+ *
+ * @throws ezcBasePropertyNotFoundException if the property does not exist.
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function __set( $name, $value )
+ {
+ switch ( $name )
+ {
+ case "source":
+ case "category":
+ $this->properties[$name] = $value;
+ return;
+ }
+
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+
+ /**
+ * Returns the property $name.
+ *
+ * @throws ezcBasePropertyNotFoundException if the property does not exist.
+ * @param string $name
+ * @return mixed
+ */
+ public function __get( $name )
+ {
+ switch ( $name )
+ {
+ case "source":
+ case "category":
+ return $this->properties[$name];
+ }
+
+ throw new ezcBasePropertyNotFoundException( $name );
+ }
+
+ /**
+ * Resets the log instance to its initial state.
+ *
+ * All sourceAttributes, severityAttributes, and writers will be removed.
+ * The default source and category are also reset.
+ *
+ * @return void
+ */
+ public function reset()
+ {
+ $this->writers = new ezcLogMap();
+ $this->context = new ezcLogContext();
+
+ $this->setDefaults();
+ }
+
+ /**
+ * Sets the source and category defaults to "default".
+ *
+ * @return void
+ */
+ protected function setDefaults()
+ {
+ $this->source = "default";
+ $this->category = "default";
+ }
+
+ /**
+ * Enables or disables writer exceptions with the boolean $enable.
+ *
+ * Typically you want to have exceptions enabled while developing your application
+ * in order to catch potential problems. A live server however, should not throw
+ * a deadly exception when a relatively unimportant debug message could not be written to
+ * the log file. For these setups you can disable writer exceptions.
+ *
+ * @param bool $enable
+ * @return void
+ */
+ public function throwWriterExceptions( $enable )
+ {
+ $this->throwWriterExceptions = $enable;
+ }
+
+ /**
+ * Write the message $message with additional information to one or multiple log writers.
+ *
+ * The log message $message, severity $severity, and extra attributes $attributes are sent to
+ * the writers that matches with the {@link ezcLogFilter}. The following parameters are
+ * taken in the comparation with the ezcLogFilter:
+ * - $severity: the severity of the log message.
+ * - $attributes[ "source" ]: the source from where the log message comes.
+ * - $attributes[ "category" ]: the category of the log message.
+ *
+ * See for more information about filter matching the classes {@link ezcLog} and
+ * {@link ezcLogFilter}.
+ *
+ * The message $message describes what happened. The severity $severity is one of the ezcLog constants:
+ * - DEBUG: Records information about the progress in the program and references
+ * source code functions. Knowledge of the source code is needed to interpret
+ * this log message.
+ * - INFO: Informative logging at a detailed level. This logging method produces a
+ * high level of logging, which is unmanageable on a production environment.
+ * Usually INFO logging is only enabled to help by analysing a problem.
+ * - NOTICE: Informative logging at a lower detail level than INFO logging.
+ * Only major stages are recorded and is useful to monitor a low volume system.
+ * - WARNING: Something unexpected happened, but did not cause any loss of service.
+ * - ERROR: An error occured, which may cause partial loss of service. Usually the
+ * system can recover.
+ * - FATAL: An serious error occured and the system is unlikely to recover.
+ * - SUCCESS_AUDIT: Informative logging about a successful completion of work by
+ * a module completed. Useful to trace system changes directly or indirectly
+ * done by a user.
+ * - FAILED_AUDIT: Informative logging about an action from a module
+ * with a negative result. A failed login will most likely added to this severity.
+ *
+ * The attributes array $attributes can have one or multiple attributes that will
+ * be added to the log. If source and category are given, they will override the default
+ * source or category given as property to this object. Further more it is up to the
+ * application what to include in the log. It may be useful to add the
+ * file and linenumber to the attributes array. Use the magic PHP constants: {@link __FILE__}
+ * and {@link __LINE__} for this purpose. The next example adds an warning to the log.
+ *
+ *
+ * ezcLog::getInstance()->source = "templateEngine"; // Set the default source.
+ * ezcLog::getInstance()->log( "ezcPersistentObject <$obj> does not exist.",
+ * ezcLog::WARNING,
+ * array( "category" => "Database", "line" => __LINE__, "file" => __FILE__, "code" => 123 )
+ * );
+ *
+ *
+ * The methods {@link setSeverityAttributes()} and {@link setSourceAttributes()} can automatically
+ * add attributes to log messages based on, respectively, the severity and source.
+ *
+ * See also {@link LogHandler()} on how to use {@link trigger_error()} to write log messages.
+ *
+ * @throws ezcLogWriterException if {@link throwWriterExceptions} are enabled and a log entry
+ * could not be written.
+ *
+ * @param string $message
+ * @param int $severity One of the following severity constants:
+ * DEBUG, SUCCES_AUDIT, FAIL_AUDIT, INFO, NOTICE, WARNING, ERROR, or FATAL.
+ * @param array(string=>string) $attributes
+ * @return void
+ */
+ public function log( $message, $severity, $attributes = array() )
+ {
+ $source = ( isset( $attributes["source"] ) ? $attributes["source"] : $this->properties["source"] );
+ $category = ( isset( $attributes["category"] ) ? $attributes["category"] : $this->properties["category"] );
+
+ unset( $attributes["source"] );
+ unset( $attributes["category"] );
+
+ $writers = $this->writers->get( $severity, $source, $category );
+ foreach ( $writers as $writer )
+ {
+ try
+ {
+ $writer->writeLogMessage( $message, $severity, $source, $category, $attributes );
+ }
+ catch ( ezcLogWriterException $e )
+ {
+ if ( $this->throwWriterExceptions )
+ {
+ throw $e;
+ }
+ }
+ }
+ }
+
+ /**
+ * Attaches the writer $writer with the filter $logFilter to this ezcLog.
+ *
+ * The log filter $logFilter describes which severities, categories, and
+ * sources are accepted by the writer $writer. Those messages that
+ * match are send to the writer.
+ *
+ * Multiple logFilters with their writer can be attached to the ezcLog class.
+ * Every log message will be sent to the writer for which the log filter matches.
+ *
+ * Available writers are:
+ * - {@link ezcLogWriterUnixFile Unix File} writer
+ * - {@link ezcLogWriterDatabase Database} writer
+ *
+ * Extra writers can be added by implementing the the {@link ezcLogWriter} interface.
+ *
+ * The following example maps the Unix file writer to all the log messages that
+ * are from the category "template", source "content_module" and severities
+ * WARNING, ERROR, or FATAL.
+ *
+ * $f = new ezclogFilter();
+ * $f->severity = ezcLog::WARNING | ezcLog::ERROR | ezcLog::FATAL;
+ * $f->source = "template";
+ * $f->category = "content_module";
+ *
+ * $w = new ezcLogWriterUnixFile("/tmp/logs/content_module/", "template.log" );
+ *
+ * $ezcLog::getInstance()->map( $f, $w );
+ *
+ *
+ * See also {@link unmap()}
+ *
+ * @param ezcLogFilter $logFilter
+ * @param ezcLogWriter $writer
+ * @return void
+ */
+ public function map( ezcLogFilter $logFilter, ezcLogWriter $writer )
+ {
+ $this->writers->map( $logFilter->severity, $logFilter->source, $logFilter->category, $writer );
+ }
+
+ /**
+ * Detaches the writer $writer for specific log messages, specified by the log filter $logFilter.
+ *
+ * The log filter $logFilter describes which severities, categories, and
+ * sources are no longer accepted by the writer $writer.
+ *
+ * See the {@link map()} method for information about attaching a filter.
+ *
+ * The following example shows how to log all messages, except the debug message:
+ *
+ * $fileWriter = ezcLogWriterUnixFile( "/tmp/logs/", "all.log" );
+ * $filter = new ezcLogFilter();
+ * $ezcLog::getInstance()->map( $filter, $fileWriter ); // All severities.
+ *
+ * $filter->severity = ezcLog::DEBUG;
+ * ezcLog::getInstance()->unmap( $filter, $fileWriter ); // Remove DEBUG severity.
+ *
+ *
+ * See also {@link map()}
+ *
+ * @param ezcLogFilter $logFilter
+ * @param ezcLogWriter $writer
+ * @return void
+ */
+ public function unmap( ezcLogFilter $logFilter, ezcLogWriter $writer )
+ {
+ $this->writers->unmap( $logFilter->severity, $logFilter->source, $logFilter->category, $writer );
+ }
+
+ /**
+ * Sets the attributes $attributes for a group of severities $severityMask.
+ *
+ * The severities are specified with a bit mask. These attributes will be
+ * added to the log message when the log severity is the same as specified
+ * here.
+ *
+ * Example:
+ *
+ * ezcLog::getInstance()->setSeverityAttributes(
+ * ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT
+ * array( "username" => "Jan K. Doodle" )
+ * );
+ *
+ *
+ * Every log message that has the severity SUCCESS_AUDIT or FAILED_AUDIT
+ * includes the user name: "Jan K. Doodle".
+ *
+ * @param integer $severityMask Multiple severities are specified with a logic-or.
+ * @param array(string=>string) $attributes
+ * @return void
+ */
+ public function setSeverityAttributes( $severityMask, $attributes )
+ {
+ $this->context->setSeverityContext( $severityMask, $attributes );
+ }
+
+ /**
+ * Sets the attributes $attributes for a group of sources $sources.
+ *
+ * The sources are specified in an array. These attributes will be added to the
+ * log message when it matches with the given $sources.
+ *
+ * Example:
+ *
+ * ezcLog::getInstance()->setSourceAttributes(
+ * array( "Paynet", "Bibit", "Paypal" ),
+ * array( "MerchantID" => $merchantID )
+ * );
+ *
+ *
+ * Every log message that comes from the payment module: Paynet, Bibit, or Paypal
+ * includes the Merchant ID.
+ *
+ * @param array(string) $sources
+ * @param array(string => string) $attributes
+ * @return void
+ */
+ public function setSourceAttributes ( $sources, $attributes )
+ {
+ $this->context->setSourceContext( $sources, $attributes );
+ }
+
+ /**
+ * This method can be set as error_handler to log using {@link trigger_error()}.
+ *
+ * This method can be assigned with the {@link set_error_handler()} to handle the
+ * trigger_error calls. This method will get the log instance and forward the
+ * message. But includes the following information:
+ * - The file and linenumber are automatically added.
+ * - Source and category can be 'encoded' in the message.
+ *
+ * The message format is as follows:
+ *
+ * [ source, category ] Message + *+ * + * When one name is given between the brackets, the category will be set and the message has a default source: + *
+ * [ category ] Message + *+ * + * Without any names between the brackets, the default category and source are used: + *
+ * Message + *+ * + * The following example creates manually an error handler and forwards the + * ERROR, WARNING and NOTICE severities. + *
+ * public function MyLogHandler($errno, $errstr, $errfile, $errline)
+ * {
+ * switch ($errno)
+ * {
+ * case E_USER_ERROR:
+ * case E_USER_WARNING:
+ * case E_USER_NOTICE:
+ * if ( $loggingEnabled )
+ * { // Forward the message to the log handler.
+ * ezcLog::LogHandler( $errno, $errstr, $errfile, $errline );
+ * }
+ * break;
+ *
+ * default:
+ * print( "$errstr in $errfile on line $errline\n" );
+ * break;
+ * }
+ * }
+ *
+ * // Register MyLogHandler
+ * set_error_handler( "MyLogHandler" );
+ *
+ * // Write an warning to the log.
+ * trigger_error( "[paynet, transaction] Didn't get a callback from the Paynet service", E_USER_WARNING );
+ *
+ * // Add a notice.
+ * trigger_error( "Getting paynet status information", E_USER_NOTICE );
+ *
+ *
+ *
+ * Notice that the ezcLog component is not loaded at all when the logging is disabled.
+ *
+ * @param int $errno
+ * @param int $erstr
+ * @param string $errfile
+ * @param int $errline
+ * @return void
+ */
+ public static function logHandler( $errno, $errstr, $errfile, $errline )
+ {
+ $log = ezcLog::getInstance();
+ $lm = new ezcLogMessage( $errstr, $errno, $log->source, $log->category );
+ $log->log(
+ $lm->message, $lm->severity,
+ array( "source" => $lm->source, "category" => $lm->category, "file" => $errfile, "line" => $errline )
+ );
+ }
+
+ /**
+ * Translates the severity constant to a string and returns this.
+ *
+ * Null is returned when the severity constant is invalid.
+ *
+ * @param int $severity
+ * @return string
+ */
+ public static function translateSeverityName( $severity )
+ {
+ switch ( $severity )
+ {
+ case self::DEBUG: return "Debug";
+ case self::SUCCESS_AUDIT: return "Success audit";
+ case self::FAILED_AUDIT: return "Failed audit";
+ case self::INFO: return "Info";
+ case self::NOTICE: return "Notice";
+ case self::WARNING: return "Warning";
+ case self::ERROR: return "Error";
+ case self::FATAL: return "Fatal";
+ default: return null;
+ }
+ }
+}
+?>
diff --git a/framework/Log/EventLog/log_autoload.php b/framework/Log/EventLog/log_autoload.php
new file mode 100644
index 00000000..06e981bc
--- /dev/null
+++ b/framework/Log/EventLog/log_autoload.php
@@ -0,0 +1,25 @@
+ 'EventLog/log.php',
+ 'ezcLogMap' => 'EventLog/map.php',
+ 'ezcLogContext' => 'EventLog/context.php',
+ 'ezcLogMessage' => 'EventLog/log_message.php',
+ 'ezcLogWriter' => 'EventLog/interfaces/writer.php',
+ 'ezcLogWriterFile' => 'EventLog/writers/writer_file.php',
+ 'ezcLogWriterUnixFile' => 'EventLog/writers/writer_unix_file.php',
+ 'ezcLogWriterDatabase' => 'EventLog/writers/writer_database.php',
+ 'ezcLogFilter' => 'EventLog/structs/log_filter.php',
+ 'ezcLogFileException' => 'EventLog/exceptions/file_exception.php',
+ 'ezcLogWriterException'=> 'EventLog/exceptions/writer_exception.php'
+);
+?>
diff --git a/framework/Log/EventLog/log_message.php b/framework/Log/EventLog/log_message.php
new file mode 100644
index 00000000..4ba61e39
--- /dev/null
+++ b/framework/Log/EventLog/log_message.php
@@ -0,0 +1,169 @@
+
+ * [ source, category ] Message
+ *
+ *
+ * When one name is given between the brackets, the category will be set and the message has a default source:
+ * + * [ category ] Message + *+ * + * Without any names between the brackets, the default category and source are used: + *
+ * Message + *+ * + * The following properties are set after construction or after calling {@link parseMessage()}: + * - message, contains the message without extra the additional information. + * - source, contains either the default source or the source set in the incoming message. + * - category, contains either the default category or the category set in the incoming message. + * - severity, severity of the error. Which is ezcLog::NOTICE, ezcLog::WARNING, or ezcLog::ERROR. + * + * @package EventLog + * @version //autogentag// + * @access private + */ +class ezcLogMessage +{ + /** + * Holds the properties of this class. + * + * @var array(string=>mixed) + */ + protected $properties = array( "message" => "", "source" => "", "category" => "", "severity" => "" ); + + + /** + * Sets the property $name to $value. + * + * @throws ezcBasePropertyNotFoundException if the property does not exist. + * @param string $name + * @param mixed $value + * @return void + */ + public function __set( $name, $value ) + { + switch ( $name ) + { + case 'message': + $this->properties['message'] = $value; + break; + case 'source': + $this->properties['source'] = $value; + break; + case 'category': + $this->properties['category'] = $value; + break; + case 'severity': + $this->properties['severity'] = $value; + break; + + default: + throw new ezcBasePropertyNotFoundException( $name ); + } + + } + + /** + * Returns the property $name. + * + * @throws ezcBasePropertyNotFoundException if the property does not exist. + * @param string $name + * @return mixed + */ + public function __get( $name ) + { + switch ( $name ) + { + case 'message': + return $this->properties['message']; + break; + case 'source': + return $this->properties['source']; + break; + case 'category': + return $this->properties['category']; + break; + case 'severity': + return $this->properties['severity']; + break; + } + + throw new ezcBasePropertyNotFoundException( $name ); + } + + /** + * Constructs the ezcLogMessage from the $message, $severity, $defaultSource and $defaultCategory. + * + * $message is parsed by parseMessage() and properties are set. + * + * @param string $message + * @param int $severity + * @param string $defaultSource Use this source when not given in the message itself. + * @param string $defaultCategory Use this category when not give in the message itself. + */ + public function __construct( $message, $severity, $defaultSource, $defaultCategory ) + { + $this->parseMessage( $message, $severity, $defaultSource, $defaultCategory ); + } + + /** + * Parses the message $message and sets the properties. + * + * See the general class documentation for message format. + * The severity $severity is a E_USER_* PHP constant. The values will be translated accordingly: + * - E_USER_NOTICE -> ezcLog::NOTICE + * - E_USER_WARNING -> ezcLog::WARNING + * - E_USER_ERROR -> ezcLog::ERROR + * + * @param string $message + * @param int $severity + * @param string $defaultSource + * @param string $defaultCategory + * @return void + */ + public function parseMessage( $message, $severity, $defaultSource, $defaultCategory ) + { + preg_match( "/^\s*(?:\[([^,\]]*)(?:,\s(.*))?\])?\s*(.*)$/", $message, $matches ); + + $this->message = ( strcmp( $matches[3], "" ) == 0 ? false : $matches[3] ); + + if ( strlen( $matches[2] ) == 0 ) + { + $this->category = ( strcmp( $matches[1], "" ) == 0 ? $defaultCategory : $matches[1] ); + $this->source = $defaultSource; + } + else + { + $this->category = $matches[2]; + $this->source = $matches[1]; + } + + switch ( $severity ) + { + case E_USER_NOTICE: $this->severity = ezcLog::NOTICE; break; + case E_USER_WARNING: $this->severity = ezcLog::WARNING; break; + case E_USER_ERROR: $this->severity = ezcLog::ERROR; break; + default: $this->severity = false; + } + } +} +?> diff --git a/framework/Log/EventLog/map.php b/framework/Log/EventLog/map.php new file mode 100644 index 00000000..2c4f72f7 --- /dev/null +++ b/framework/Log/EventLog/map.php @@ -0,0 +1,386 @@ +bitmask) + */ + protected $structure; + + + public function __construct() + { + $this->result = array(); + } + + /** + * Map the object $object to the given $eventTypeMask, $eventSources and $eventCategories. + * + * Example: + *
+ * [ eventType x eventSource x eventCategory ] |-> [ object ]
+ *
+ *
+ * @param int $eventTypeMask
+ * @param array(string) $eventSources
+ * @param array(string) $eventCategories
+ * @param object $object
+ *
+ * @return void
+ */
+ public function map( $eventTypeMask, $eventSources, $eventCategories, $object )
+ {
+ $eventTypes = $this->getMaskArray( $eventTypeMask );
+ $result = array( $object );
+
+ if ( count( $eventTypes ) == 0 )
+ {
+ $eventTypes = array( "*" );
+ }
+ if ( count( $eventSources ) == 0 )
+ {
+ $eventSources = array( "*" );
+ }
+ if ( count( $eventCategories ) == 0)
+ {
+ $eventCategories = array( "*" );
+ }
+ $c = $this->createMinimumHash( $eventCategories, $result );
+ $s = $this->createMinimumHash( $eventSources, $c );
+ $new = $this->createMinimumHash( $eventTypes, $s );
+
+ $this->mergeHash( $new, $this->structure, $history );
+ }
+
+ /**
+ * Unmap the object $object to the given $eventTypeMask, $eventSources and $eventCategories.
+ *
+ * @param int $eventTypeMask
+ * @param array(string) $eventSources
+ * @param array(string) $eventCategories
+ * @param object $object
+ *
+ * @return void
+ */
+ public function unmap( $eventTypeMask, $eventSources, $eventCategories, $object)
+ {
+ $eventTypes = $this->getMaskArray( $eventTypeMask );
+
+ $result = array( $object );
+
+ if ( count( $eventTypes ) == 0 )
+ {
+ $eventTypes = array( "*" );
+ }
+ if ( count( $eventSources ) == 0 )
+ {
+ $eventSources = array( "*" );
+ }
+ if ( count( $eventCategories ) == 0)
+ {
+ $eventCategories = array( "*" );
+ }
+
+ $c = $this->createMinimumHash( $eventCategories, $result );
+ $s = $this->createMinimumHash( $eventSources, $c );
+ $new = $this->createMinimumHash( $eventTypes, $s );
+
+ $this->unmergeHash( $this->structure, $new, $history );
+ }
+
+
+ /**
+ * Fills the array $out with the keys $inKeys and the value $inValue.
+ *
+ * @param array inKeys
+ * @param mixed $inValue
+ * @param array &$out
+ * @return array
+ */
+ protected function createMinimumHash($inKeys, $inValue )
+ {
+ foreach ( $inKeys as $key )
+ {
+ $out[$key] = $inValue;
+ }
+ return $out;
+ }
+
+ /**
+ *
+ *
+ * @return void
+ **/
+ protected function mergeSingle( &$in, &$history )
+ {
+ $extra = $this->get( $history[0], $history[1], $history[2] );
+ $extra = array_merge( $in, $extra );
+
+ $tmp =& $this->structure[$history[0]];
+ $tmp =& $tmp[$history[1]];
+ $tmp =& $tmp[$history[2]];
+
+ foreach ( $extra as $inKey => $inVal )
+ {
+ if ( !isset( $tmp ) || !in_array( $inVal, $tmp ) )
+ {
+ if ( is_numeric( $inKey ) )
+ {
+ $tmp[] = $inVal;
+ }
+ else
+ {
+ $tmp[$inKey] =& $inVal;
+ }
+ }
+ }
+ }
+
+
+ // XXX should change out.. the way it is now.
+ protected function mergeHash( &$in, $out, &$history )
+ {
+ $depth = count( $history );
+
+ if ( $depth == 3 )
+ {
+ $this->mergeSingle( $in, $history );
+ }
+
+ $i = 0;
+ foreach ( $in as $inKey => $inVal )
+ {
+ $history[$depth] = $inKey;
+
+ if ( strcmp( $inKey, "*" ) == 0)
+ {
+ if ( is_array( $out ) )
+ {
+ foreach ( $out as $outKey => $outVal )
+ {
+ $history[$depth] = $outKey;
+ $this->mergeHash( $in[$inKey], $out[$outKey], $history );
+ }
+ }
+ }
+
+ $history[$depth] = $inKey;
+ if ( is_array( $inVal ) )
+ {
+ if ( is_numeric( $inKey ) )
+ {
+ // FIXME
+ $this->mergeHash( $in[$inKey], $out, $history );
+ }
+ else
+ {
+ //$this->mergeHash( $in[$inKey], $out[$inKey], $history );
+ $this->mergeHash( $in[$inKey], $out, $history );
+ }
+ }
+
+ }
+ array_pop( $history );
+ }
+
+
+ // XXX: ugly, should get a rewrite.
+ protected function unmergeHash( &$out, &$in, &$history )
+ {
+ $depth = count( $history );
+
+ if ( $depth == 3 )
+ {
+ foreach ( $in as $inKey => $inVal )
+ {
+ if ( !isset( $out ) || in_array( $inVal, $out ) )
+ {
+ if ( is_array( $out ) )
+ {
+ foreach ( $out as $outKey => $outVal )
+ {
+ if ( $outVal === $inVal )
+ {
+ unset( $out[$outKey] );
+ $result = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ foreach ( $in as $inKey => $inVal )
+ {
+ $history[$depth] = $inKey;
+
+ if ( strcmp( $inKey, "*" ) == 0)
+ {
+ if ( is_array( $out ) )
+ {
+ foreach ( $out as $outKey => $outVal )
+ {
+ $this->unmergeHash( $out[$outKey], $in[$inKey], $history );
+ }
+ }
+ }
+
+ if ( is_array( $inVal ) )
+ {
+ $this->unmergeHash( $out[$inKey], $in[$inKey], $history );
+ }
+ }
+ array_pop( $history );
+ }
+
+
+ /**
+ * Returns the bits set in $mask as separate values in an array.
+ *
+ * @return array(int)
+ */
+ protected function getMaskArray( $mask )
+ {
+ $result = array();
+
+ $input = 1;
+ while ( $input <= $mask )
+ {
+ if ( $mask & $input )
+ {
+ $result[] = $input;
+ }
+ $input <<= 1;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Returns an array with objects are mapped to the key generated by $eventType, $eventSource and $eventCategory.
+ *
+ * The eventType is an integer from in the range: 2^x with x -> N+.
+ * (1, 2, 4, 8, 16, ... )
+ *
+ * @param int $eventType
+ * @param string $eventSource
+ * @param string $eventCategory
+ * @return array(object).
+ */
+ public function get( $eventType, $eventSource, $eventCategory)
+ {
+ $tmp = $this->structure;
+
+
+ if ( isset( $tmp[$eventType] ) )
+ {
+ $tmp = $tmp[$eventType];
+ }
+ else if ( isset( $tmp [ "*" ] ) )
+ {
+ $tmp = $tmp["*"];
+ }
+ else
+ {
+ return array();
+ }
+
+ if ( isset( $tmp[$eventSource] ) )
+ {
+ $tmp = $tmp[$eventSource];
+ }
+ else if ( isset( $tmp [ "*" ] ) )
+ {
+ $tmp = $tmp["*"];
+ }
+ else
+ {
+ return array();
+ }
+
+ if ( isset( $tmp[$eventCategory] ) )
+ {
+ $tmp = $tmp[$eventCategory];
+ }
+ else if ( isset( $tmp [ "*" ] ) )
+ {
+ $tmp = $tmp["*"];
+ }
+ else
+ {
+ return array();
+ }
+
+ return $tmp;
+ }
+
+
+ public function printStructure()
+ {
+ $this->printStructureRecursive( $this->structure, $history = array() );
+ }
+
+ protected function printStructureRecursive( $structure, &$history )
+ {
+ $depth = count( $history );
+ if ( $depth == 3 )
+ {
+ $tmp =& $this->structure[$history[0]];
+ $tmp =& $tmp[$history[1]];
+ $tmp =& $tmp[$history[2]];
+
+ if ( isset( $tmp ) )
+ {
+ foreach ( $tmp as $key => $val )
+ {
+ echo $history[0] . " " . $history[1] . " " . $history[2] . " [ $key => $val ]\n";
+ }
+ }
+
+ return;
+ }
+
+ foreach ( $structure as $key => $val )
+ {
+ $history[$depth] = $key;
+ $this->printStructureRecursive( $structure, $history );
+ }
+ }
+}
+?>
diff --git a/framework/Log/EventLog/structs/log_filter.php b/framework/Log/EventLog/structs/log_filter.php
new file mode 100644
index 00000000..451b0c65
--- /dev/null
+++ b/framework/Log/EventLog/structs/log_filter.php
@@ -0,0 +1,74 @@
+
diff --git a/framework/Log/EventLog/writers/writer_database.php b/framework/Log/EventLog/writers/writer_database.php
new file mode 100644
index 00000000..6908a770
--- /dev/null
+++ b/framework/Log/EventLog/writers/writer_database.php
@@ -0,0 +1,216 @@
+db = $databaseInstance;
+
+ $this->map = new ezcLogMap();
+ $this->defaultTable = $defaultTable;
+
+ $this->message = "message";
+ $this->datetime = "time";
+ $this->severity = "severity";
+ $this->source = "source";
+ $this->category = "category";
+ }
+
+ /**
+ * Sets the property $name to $value.
+ *
+ * @throws ezcBasePropertyNotFoundException if the property does not exist.
+ * @param string $name
+ * @param mixed $value
+ * @return void
+ */
+ public function __set( $name, $value )
+ {
+ switch ( $name )
+ {
+ case 'table': $this->properties['table'] = $value; break;
+ case 'message':
+ case 'datetime':
+ case 'severity':
+ case 'source':
+ case 'category': $this->defaultColumns[ $name ] = $value; break;
+ default: $this->additionalColumns[ $name ] = $value; break;
+ }
+ }
+
+ /**
+ * Returns the property $name.
+ *
+ * @throws ezcBasePropertyNotFoundException if the property does not exist.
+ * @param string $name
+ * @return mixed
+ */
+ public function __get( $name )
+ {
+ switch ( $name )
+ {
+ case 'table': return $this->properties['table']; break;
+ case 'message':
+ case 'datetime':
+ case 'severity':
+ case 'source':
+ case 'category': return $this->defaultColumns[ $name ]; break;
+
+ default: return $this->additionalColumns[ $name ]; break;
+ }
+ }
+
+ /**
+ * Writes the message $message to the log.
+ *
+ * The writer can use the severity, source, and category to filter the
+ * incoming messages and determine the location where the messages should
+ * be written.
+ *
+ * $optional may contain extra information that can be added to the log. For example:
+ * line numbers, file names, usernames, etc.
+ *
+ * @throws ezcLogWriterException when the log writer was unable to write
+ * the log message.
+ * @param string $message
+ * @param int $severity
+ * ezcLog:: DEBUG, SUCCES_AUDIT, FAILED_AUDIT, INFO, NOTICE, WARNING, ERROR or FATAL.
+ *
+ * $param string $source
+ * @param string $category
+ * @param array(string=>string) $optional
+ * @return void
+ */
+ public function writeLogMessage( $message, $severity, $source, $category, $optional = array() )
+ {
+ $severityName = ezcLog::translateSeverityName( $severity );
+
+ $colStr = "";
+ $valStr = "";
+
+ if ( is_array( $optional ) )
+ {
+ foreach ( $optional as $key => $val )
+ {
+ $colStr .= ", " . ( isset( $this->additionalColumns[$key] ) ? $this->additionalColumns[$key] : $key );
+ $valStr .= ", " . $this->db->quote( $val );
+ }
+ }
+
+ $tables = $this->map->get( $severity, $source, $category );
+
+ $query = $this->db->createSelectQuery();
+ if ( count( $tables ) > 0)
+ {
+ foreach ( $tables as $t )
+ {
+ try
+ {
+ $this->db->exec( "INSERT INTO `{$t}` ( {$this->message}, {$this->severity}, {$this->source}, {$this->category}, {$this->datetime} $colStr ) ".
+ "VALUES( ".$this->db->quote( $message ).", ".$this->db->quote( $severityName ).", ".$this->db->quote( $source ).", ".
+ $this->db->quote( $category ).", ".$query->expr->now()." $valStr )" );
+ }
+ catch ( PDOException $e )
+ {
+ throw new ezcLogWriterException( "SQL query failed in ezcLogWriterDatabase.\n". $e->getMessage() );
+ }
+ }
+ }
+ else
+ {
+ if ( $this->defaultTable !== false )
+ {
+ try
+ {
+ $this->db->exec( "INSERT INTO `{$this->defaultTable}` ( {$this->message}, {$this->severity}, {$this->source}, {$this->category}, {$this->datetime} $colStr ) ".
+ "VALUES( ".$this->db->quote( $message ).", ".$this->db->quote( $severityName ).", ".$this->db->quote( $source ).", ".
+ $this->db->quote( $category ).", ".$query->expr->now()." $valStr )" );
+ }
+ catch ( PDOException $e )
+ {
+ throw new ezcLogWriterException( "SQL query failed in ezcLogWriterDatabase.\n". $e->getMessage() );
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns an array that describes the coupling between the logMessage
+ * information and the columns in the database.
+ *
+ * @return array(string=>string)
+ */
+ public function getColumnTranslations()
+ {
+ return array_merge( $this->defaultColumns, $this->additionalColumns );
+ }
+
+
+ /**
+ * Maps the table $tableName to the messages specified by the {@link ezcLogFilter} $logFilter.
+ *
+ * Log messages that matches with the filter are written to the table $tableName.
+ * This method works the same as {@link ezclog::map()}.
+ *
+ * @param ezcLogFilter $logFilter
+ * @param string $tableName
+ * @return void
+ */
+ public function map( ezcLogFilter $logFilter, $tableName )
+ {
+ $this->map->map( $logFilter->severity, $logFilter->source, $logFilter->category, $tableName );
+ }
+
+ /**
+ * Unmaps the table $tableName from the messages specified by the {@link ezcLogFilter} $logFilter.
+ *
+ * Log messages that matches with the filter are no longer written to the table $tableName.
+ * This method works the same as {@link ezclog::unmap()}.
+ *
+ * @param ezcLogFilter $logFilter
+ * @param string $fileName
+ * @return void
+ */
+ public function unmap( ezcLogFilter $logFilter, $tableName )
+ {
+ $this->map->unmap( $logFilter->severity, $logFilter->source, $logFilter->category, $tableName );
+ }
+}
+
+?>
diff --git a/framework/Log/EventLog/writers/writer_file.php b/framework/Log/EventLog/writers/writer_file.php
new file mode 100644
index 00000000..46ecf13e
--- /dev/null
+++ b/framework/Log/EventLog/writers/writer_file.php
@@ -0,0 +1,273 @@
+
+ * class MyLogWriter extends ezcLogWriterFile
+ * {
+ * // Call parent constructor. (In this case, it possible to omit the constructor.)
+ * public function __construct($dir, $file = null, $maxSize = 204800, $maxFiles = 3 )
+ * {
+ * parent::__construct($dir, $file, $maxSize, $maxFiles);
+ * }
+ *
+ * // Implement the ezcLogWriter interface:
+ * public function writeLogMessage( $message, $type, $source, $category, $extraInfo = array() )
+ * {
+ * // Create a message
+ * $res = print_r( array( "message" => $message, "type" => $type, "source" => $source, "category" => $category ), true );
+ *
+ * // And call the parent class
+ * $this->write( $type, $source, $category, $res );
+ * }
+ *}
+ *
+ *
+ * @package EventLog
+ * @version //autogentag//
+ */
+abstract class ezcLogWriterFile implements ezcLogWriter
+{
+ /**
+ * Contains all the open files. The first file in the
+ * array is always the default file.
+ *
+ * @var array(resource)
+ */
+ protected $openFiles = array();
+
+
+ /**
+ * Keeps track of which group of messages should be stored
+ * in what file.
+ *
+ * @var ezcLogMap
+ */
+ protected $fileMap;
+
+ /**
+ * Directory where the log files should be placed.
+ *
+ * @var string
+ */
+ protected $logDirectory;
+
+ /**
+ * Maximum file size before rotation.
+ *
+ * @var int
+ */
+ protected $maxSize;
+
+ /**
+ * Maximum log rotation files with the same name.
+ *
+ * When rotating and the max limit is reached, the oldest log
+ * is discarded.
+ *
+ * @var int
+ */
+ protected $maxFiles;
+
+
+ /**
+ * Constructs an ezcLogFileWriter.
+ *
+ * The log files will be placed in the directory $logDirectory.
+ *
+ * If the file $defaultFile is not null, log messages that are not {@link map() mapped}
+ * to any file are written to this $defaultFile. If $defaultFile is null, then
+ * log messages are discarded.
+
+ *
+ * Set $maxLogRotationSize to specify the maximum size of a logfile. When the
+ * maximum size is reached, the log will be rotated. $maxLogFiles sets the maximum
+ * number of rotated log files. The oldest rotated log will be removed when the
+ * maxLogFiles exceeds.
+ *
+ * @param string $logDirectory
+ * @param string $defaultFile
+ * @param string $maxLogRotationSize
+ * @param string $maxLogFiles
+ */
+ public function __construct( $logDirectory, $defaultFile = null, $maxLogRotationSize = 204800, $maxLogFiles = 3 )
+ {
+ $this->maxSize = $maxLogRotationSize;
+ $this->maxFiles = $maxLogFiles;
+ $this->logDirectory = $logDirectory;
+ $this->defaultFile = $defaultFile;
+
+ if ( !is_null( $defaultFile ) )
+ {
+ $this->openFile( $defaultFile );
+ }
+
+ $this->fileMap = new ezcLogMap();
+ }
+
+ /**
+ * Destructs the object and closes all open file handles.
+ */
+ public function __destruct()
+ {
+ foreach ( $this->openFiles as $fh )
+ {
+ fclose( $fh );
+ }
+ }
+
+
+ /**
+ * This method writes the $string to a file.
+ *
+ * The file to which the string will be written depends on the $eventType, $eventSource, and
+ * $eventCategory.
+ *
+ * @throws ezcLogWriterException if it was not possible to write to the log file.
+ * @param int $eventType
+ * @param string $eventSource
+ * @param string $eventCategory
+ * @param string $string
+ * @return void
+ */
+ protected function write( $eventType, $eventSource, $eventCategory, $string )
+ {
+ $fileHandles = $this->fileMap->get( $eventType, $eventSource, $eventCategory );
+
+ if ( count( $fileHandles ) > 0 )
+ {
+ foreach ( $fileHandles as $fh )
+ {
+ if ( fwrite( $fh, $string ) === false)
+ {
+ throw ezcLogWriterException( "Cannot write to the attached log file.", ezcLogWriterException::FILE_NOT_WRITABLE );
+ }
+ }
+ }
+ else
+ {
+ if ( !is_null( $this->defaultFile ) )
+ {
+ if ( fwrite( $this->openFiles[$this->defaultFile], $string ) === false )
+ {
+ throw ezcLogWriterException( "Cannot write to the default log file.", ezcLogWriterException::FILE_NOT_WRITABLE );
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the filehandle of the $fileName.
+ *
+ * If the maximum file size is exceeded, the file will be rotated before opening.
+ *
+ * @return resource
+ */
+ protected function openFile( $fileName )
+ {
+ if ( isset( $this->openFiles[$fileName] ) )
+ {
+ return $this->openFiles[$fileName];
+ }
+
+ clearstatcache();
+ if ( file_exists( $this->logDirectory . "/". $fileName ) &&
+ ( filesize( $this->logDirectory . "/". $fileName ) >= $this->maxSize ) )
+ {
+ $this->rotateLog( $fileName );
+ }
+
+ $fh = @fopen( $this->logDirectory ."/". $fileName, "w" );
+ if ( $fh === false )
+ {
+ // throw exception.
+ throw new ezcLogFileException( "Cannot open the file <{$fileName}> for writing", ezcLogFileException::FILE_NOT_FOUND );
+ }
+
+ $this->openFiles[$fileName] = $fh;
+ return $fh;
+ }
+
+ /**
+ * Rotates a log and returns true upon success.
+ *
+ * @return bool
+ */
+ protected function rotateLog( $fileName )
+ {
+ $file = $this->logDirectory . "/" . $fileName;
+
+ for ( $i = $this->maxFiles; $i > 0; --$i )
+ {
+ $logRotateName = $file. '.' . $i;
+ if ( file_exists( $logRotateName ) )
+ {
+ if ( $i == $this->maxFiles )
+ {
+ unlink( $logRotateName );
+ }
+ else
+ {
+ $newLogRotateName = $file . '.' . ( $i + 1 );
+ rename( $logRotateName, $newLogRotateName );
+ }
+ }
+ }
+ if ( file_exists( $file ) )
+ {
+ $newLogRotateName = $file . '.' . 1;
+ rename( $file, $newLogRotateName );
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Maps the filename $fileName to the messages specified by the {@link ezcLogFilter} $logFilter.
+ *
+ * Log messages that matches with the filter are written to the file $fileName.
+ * This method works the same as {@link ezclog::map()}.
+ *
+ * @param ezcLogFilter $logFilter
+ * @param string $fileName
+ * @return void
+ */
+ public function map( ezcLogFilter $logFilter, $fileName )
+ {
+ $fh = $this->openFile( $fileName );
+ $this->fileMap->map( $logFilter->severity, $logFilter->source, $logFilter->category, $fh );
+ }
+
+ /**
+ * Unmaps the filename $fileName from the messages specified by the {@link ezcLogFilter} $logFilter.
+ *
+ * Log messages that matches with the filter are no longer written to the file $fileName.
+ * This method works the same as {@link ezclog::unmap()}.
+ *
+ * @param ezcLogFilter $logFilter
+ * @param string $fileName
+ * @return void
+ */
+ public function unmap( $logFilter, $fileName )
+ {
+ $this->fileMap->unmap( $logFilter->severity, $logFilter->source, $logFilter->category, $this->openFiles[ $fileName ] );
+ }
+
+}
+?>
diff --git a/framework/Log/EventLog/writers/writer_unix_file.php b/framework/Log/EventLog/writers/writer_unix_file.php
new file mode 100644
index 00000000..a2045217
--- /dev/null
+++ b/framework/Log/EventLog/writers/writer_unix_file.php
@@ -0,0 +1,100 @@
+
+ * MMM dd HH:mm:ss [Severity] [Source] [Category] Message (ExtraInfo)
+ *
+ *
+ * With:
+ * - MMM: The 3 letter abbreviation of the month.
+ * - dd: The day of the month.
+ * - HH: The hour.
+ * - mm: The minutes.
+ * - ss: The seconds.
+ *
+ * Example:
+ * + * Jan 24 15:32:56 [Debug] [Paynet] [Shop] Connecting to the paynet server (file: paynet_server.php, line: 224). + * Jan 24 15:33:01 [Debug] [Paynet] [Shop] Connected with the server (file: paynet_server.php, line: 710). + *+ * + * This method will be called by the {@link ezcLog} class. The $eventSource and $eventCategory are either given + * in the {@link ezcLog::log()} method or are the defaults from the {@link ezcLog} class. + * + * @param string $message + * @param int $eventType + * @param string $eventType + * @param string $eventCategory + * @param array(string=>string) $extraInfo + */ + public function writeLogMessage( $message, $eventType, $eventSource, $eventCategory, $extraInfo = array() ) + { + $extra = $this->implodeWithKey( ", ", ": ", $extraInfo ); + + if ( $eventCategory == false ) + { + $eventCategory = ""; + } + $logMsg = @date( "M d H:i:s" ) . + " [".ezcLog::translateSeverityName( $eventType ) . + "] [$eventSource] [$eventCategory] $message ($extra)\n"; + + $this->write( $eventType, $eventSource, $eventCategory, $logMsg ); + } + + /** + * Returns a string from the hash $data. + * + * The string $splitEntry specifies the string that will be inserted between the pairs. + * The string $splitKeyVal specifies the string that will be inserted in each pair. + * + * Example: + *
+ * $this->implodeWithKey( ", ", ": ", array( "Car" => "red", "Curtains" => "blue" );
+ *
+ *
+ * Will create the following string:
+ * + * Car: red, Curtains: blue + *+ * + * @param string $splitEntry + * @param string $splitKeyVal + * @param array(mixed=>mixed) $data + * @return string + */ + protected function implodeWithKey( $splitEntry, $splitKeyVal, $data) + { + $total = ""; + if ( is_array( $data ) ) + { + foreach ( $data as $key => $val ) + { + $total .= $splitEntry . $key . $splitKeyVal . $val; + } + } + + return substr( $total, strlen( $splitEntry ) ); + } +} +?> -- cgit v1.2.3