diff options
-rw-r--r-- | .gitattributes | 15 | ||||
-rw-r--r-- | framework/Log/EventLog/context.php | 167 | ||||
-rw-r--r-- | framework/Log/EventLog/exceptions/file_exception.php | 44 | ||||
-rw-r--r-- | framework/Log/EventLog/exceptions/writer_exception.php | 30 | ||||
-rw-r--r-- | framework/Log/EventLog/interfaces/writer.php | 45 | ||||
-rw-r--r-- | framework/Log/EventLog/log.php | 644 | ||||
-rw-r--r-- | framework/Log/EventLog/log_autoload.php | 25 | ||||
-rw-r--r-- | framework/Log/EventLog/log_message.php | 169 | ||||
-rw-r--r-- | framework/Log/EventLog/map.php | 386 | ||||
-rw-r--r-- | framework/Log/EventLog/structs/log_filter.php | 74 | ||||
-rw-r--r-- | framework/Log/EventLog/writers/writer_database.php | 216 | ||||
-rw-r--r-- | framework/Log/EventLog/writers/writer_file.php | 273 | ||||
-rw-r--r-- | framework/Log/EventLog/writers/writer_unix_file.php | 100 | ||||
-rw-r--r-- | framework/Log/TEventLog.php | 56 | ||||
-rw-r--r-- | framework/Log/TEzcLogger.php | 133 | ||||
-rw-r--r-- | framework/Log/TLog.php | 13 |
16 files changed, 2390 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes index 985127e8..0175b999 100644 --- a/.gitattributes +++ b/.gitattributes @@ -132,6 +132,21 @@ framework/Exceptions/templates/exception-zh.html -text framework/Exceptions/templates/exception.html -text framework/Exceptions/templates/readme.txt -text framework/IO/TTextWriter.php -text +framework/Log/EventLog/context.php -text +framework/Log/EventLog/exceptions/file_exception.php -text +framework/Log/EventLog/exceptions/writer_exception.php -text +framework/Log/EventLog/interfaces/writer.php -text +framework/Log/EventLog/log.php -text +framework/Log/EventLog/log_autoload.php -text +framework/Log/EventLog/log_message.php -text +framework/Log/EventLog/map.php -text +framework/Log/EventLog/structs/log_filter.php -text +framework/Log/EventLog/writers/writer_database.php -text +framework/Log/EventLog/writers/writer_file.php -text +framework/Log/EventLog/writers/writer_unix_file.php -text +framework/Log/TEventLog.php -text +framework/Log/TEzcLogger.php -text +framework/Log/TLog.php -text framework/Security/TAuthManager.php -text framework/Security/TAuthorizationRule.php -text framework/Security/TMembershipManager.php -text 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 @@ +<?php +/** + * File containing the ezcLogContext class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @access private + */ + +/** + * Stores the contexts for the severities and sources. + * + * @package EventLog + * @version //autogentag// + * @access private + */ +class ezcLogContext +{ + + /** + * Stores the contexts for the severities. + * + * @var array(integer=>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 @@ +<?php +/** + * File containing the ezcFileWriterException class. + * + * @package EventLog + * @version //autogen// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ +/** + * Exception thrown when a file error occures. + * + * @package EventLog + * @version //autogen// + */ +class ezcLogFileException extends Exception +{ + /** + * The file could not be found on the filesystem. + */ + const FILE_NOT_FOUND = 1; + + /** + * The file could not be read from the filesystem. + */ + const FILE_NOT_READABLE = 2; + + /** + * The file could not be written to the filesystem. + */ + const FILE_NOT_WRITABLE = 3; + + /** + * Constructs a new ezcLogFileExcpetion with the message $message and the error code $code. + * + * @param string $message + * @param int $code + */ + public function __construct( $message, $code ) + { + parent::__construct( $message, $code ); + } +} +?> 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 @@ +<?php +/** + * File containing the ezcLogWriterException class. + * + * @package EventLog + * @version //autogen// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * The ezcLogWriterException will be thrown when an {@class ezcLogWriter} or + * a subclass encounters an exceptional state. + * + * @package EventLog + * @version //autogen// + */ +class ezcLogWriterException extends Exception +{ + /** + * Constructs a new ezcLogWriterException with the message $message. + * + * @param string $message + */ + public function __construct( $message ) + { + parent::__construct( $message, 0 ); + } +} +?> 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 @@ +<?php +/** + * File containing the ezcLogWriter interface. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * ezcLogWriter defines the common interface for all classes that implement + * their log writer. + * + * See the ezcLogFileWriter for an example of creating your own log writer. + * + * @package EventLog + * @version //autogentag// + */ +interface ezcLogWriter +{ + /** + * 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. + * + * The array $optional contains 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, ezcLog::SUCCES_AUDIT, ezcLog::FAIL_AUDIT, ezcLog::INFO, ezcLog::NOTICE, + * ezcLog::WARNING, ezcLog::ERROR or ezcLog::FATAL. + * $param string $source + * @param string $category + * @param array(string=>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 @@ +<?php +/** + * File containing the ezcLog class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * The ezcLog class records log messages and audit trails to one or multiple + * writers. + * + * Available writers are: + * - {@link ezcLogWriterUnixFile Unix File} writer + * - {@link ezcLogWriterDatabase Database} writer + * + * Extra writers can be added by implementing the the {@link ezcLogWriter} interface. + * + * Use the {@link map()} method to attach an additional writer to the ezcLog + * class. This method filters incoming log messages with the {@link ezcLogFilter}. + * Log messages that are accepted, match with the filter, are sent to the + * {@link ezcLogWriter}. + * + * The following example demonstrates how all log messages, except for the + * audit trailing and debug messages, are written to a file. + * <code> + * $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" ) ); + * </code> + * + * 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: + * <code> + * $filter = new ezcLogFilter(); + * $filter->severity = ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT; + * + * $log = ezcLog::getInstance(); + * $log->map( $filter, new ezcLogWriterDatabase( "audits" ) ); + * </code> + * + * 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: + * <code> + * $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. + * </code> + * + * 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: + * <code> + * ezcLog::getInstance()->log( "Cannot open ini file: <$file>", ezcLog::FATAL ); + * </code> + * + * 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: + * <code> + * // 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(); + * } + * </code> + * + * 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. + * + * <code> + * 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 ) + * ); + * </code> + * + * 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. + * <code> + * $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 ); + * </code> + * + * 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: + * <code> + * $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. + * </code> + * + * 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: + * <code> + * ezcLog::getInstance()->setSeverityAttributes( + * ezcLog::SUCCESS_AUDIT | ezcLog::FAILED_AUDIT + * array( "username" => "Jan K. Doodle" ) + * ); + * </code> + * + * 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: + * <code> + * ezcLog::getInstance()->setSourceAttributes( + * array( "Paynet", "Bibit", "Paypal" ), + * array( "MerchantID" => $merchantID ) + * ); + * </code> + * + * 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: + * <pre> + * [ source, category ] Message + * </pre> + * + * When one name is given between the brackets, the category will be set and the message has a default source: + * <pre> + * [ category ] Message + * </pre> + * + * Without any names between the brackets, the default category and source are used: + * <pre> + * Message + * </pre> + * + * The following example creates manually an error handler and forwards the + * ERROR, WARNING and NOTICE severities. + * <code> + * 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 ); + * + * </code> + * + * 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 @@ +<?php +/** + * Autoloader definition for the Translation component. + * + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @version //autogentag// + * @filesource + * @package EventLog + */ + +return array( + 'ezcLog' => '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 @@ +<?php +/** + * File containing the ezcLogMessage class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @access private + */ + +/** + * Holds a log message and provides convenience methods to read the information. + * + * The ezclogMessage class is used for subtracting the information from the message + * parameter from {@link trigger_error()}. See the {@link ezcLog::logHandler} for + * more information. + * + * The message formats that can be parsed are: + * <pre> + * [ source, category ] Message + * </pre> + * + * When one name is given between the brackets, the category will be set and the message has a default source: + * <pre> + * [ category ] Message + * </pre> + * + * Without any names between the brackets, the default category and source are used: + * <pre> + * Message + * </pre> + * + * 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 @@ +<?php +/** + * File containing the ezcLogMap class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + * @access private + */ + +/** + * Fast mapping of a mixed variable to an eventType, eventSource and eventCategory. + * + * This class is used in ezcLog to map writers to the log messages they + * are supposed to write. It is important that the get() method is extremely + * fast since it will be called for each log message. + * + * If every combination of eventTypes, eventSources, and eventCategories are + * combined (product) and placed in one hash-table. The table grows too fast. + * Imagine: 7 types * 10 sources * 10 categories = 700 entries in the hash. + * + * Our solution: + * Most probably the amount of writers will be limited. So every writer + * (result) is one bit (of an integer). + * + * The hashes: Type, Source, and Category map the input strings to an integer. + * These integers are AND with each other, and the result is again stored in a + * hash. + * + * + * @package EventLog + * @version //autogentag// + * @access private + */ +class ezcLogMap +{ + /** + * Hashtable that binds a single eventType to a bitmask. The possible + * key-values are: 1, 2, 4, 8, ... + * + * @var array(integer=>bitmask) + */ + protected $structure; + + + public function __construct() + { + $this->result = array(); + } + + /** + * Map the object $object to the given $eventTypeMask, $eventSources and $eventCategories. + * + * Example: + * <code> + * [ eventType x eventSource x eventCategory ] |-> [ object ] + * </code> + * + * @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 @@ +<?php + +/** + * File containing the ezcLogFilter class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license BSD {@link http://ez.no/licenses/bsd} + */ + +/** + * The ezcLogFilter class provides a structure to set a log filter. + * + * ezcLogFilter has three public member variables: + * - severity, contains the severity of the log message. + * - source, contain the source of the log message. + * - category, contains the category of the log message. + * + * Severity is an integer mask that expects one more multiple ezcLog severity constants. + * Multiple values can be assigned by using a logical-or on the values. The value zero + * represents all possible severities. + * + * Source and category are an array. An empty array reprseents all possible sources + * and categories. + * + * The ezclogFilter class is mainly used by the {@link ezcLog::attach()} and {@link ezcLog::detach()} + * methods. + * + * @package EventLog + * @version //autogentag// + */ +class ezcLogFilter +{ + /** + * The severities that are accepted by the ezcLogFilter. + * + * The default value zero specifies that all severities are accepted. + * + * @var int + */ + public $severity = 0; + + + /** + * The source of the log message. + * + * The default empty array specifies that all sources are accepted by this filter. + * + * @var array(string) + */ + public $source = array(); + + /** + * The category of the log message. + * + * The default empty array specifies that all categories are accepted by this filter. + * + * @var array(string) + */ + public $category = array(); + + + /** + * Empty constructor + */ + public function __construct() + { + } +} + + + +?> 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 @@ +<?php +/** + * File containing the ezcLogWriterDatabase class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Writes log messages to the database. + * + * @package EventLog + * @version //autogentag// + */ +class ezcLogWriterDatabase implements ezcLogWriter +{ + private $db = null; + + private $properties = array(); + private $defaultColumns = array(); + private $additionalColumns = array(); + + private $map; + private $defaultTable = false; + + /** + * Construct a new database log-writer. + * + * You can set the default table to write to with the $defaultTable parameter. + * If $databaseInstance is given, that instance will be used for writing. If it + * is ommitted the default database instance will be retrieved. + * + * This constructor is a tie-in. + * + * @param string $defaultTable + * @param ezcDbHandler $databaseInstance + * + */ + public function __construct( ezcDbHandler $databaseInstance, $defaultTable = false ) + { + $this->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 @@ +<?php +/** + * File containing the ezcLogWriterFile class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * The ezcLogWriterFile class provides functionality to write log files to the file + * system. + * + * The main purpose is to keep track of the various log files and support + * log rotation. The file format of the log should be implemented in a subclass. + * + * The following example implements a new log writer that writes the output in ({@link print_r()} format) + * to a file: + * <code> + * 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 ); + * } + *} + * </code> + * + * @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 @@ +<?php +/** + * File containing the ezcLogWriterUnix class. + * + * @package EventLog + * @version //autogentag// + * @copyright Copyright (C) 2005, 2006 eZ systems as. All rights reserved. + * @license http://ez.no/licenses/new_bsd New BSD License + */ + +/** + * Writes the log messages to a file in a format that is frequently used on the Unix operating system. + * + * @package EventLog + * @version //autogentag// + */ +class ezcLogWriterUnixFile extends ezcLogWriterFile +{ + /** + * Write the logEntries to a file. + * + * Each line in the log file represents a log message. The log + * messages have the following style: + * <pre> + * MMM dd HH:mm:ss [Severity] [Source] [Category] Message (ExtraInfo) + * </pre> + * + * 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: + * <pre> + * 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). + * </pre> + * + * 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: + * <code> + * $this->implodeWithKey( ", ", ": ", array( "Car" => "red", "Curtains" => "blue" ); + * </code> + * + * Will create the following string: + * <pre> + * Car: red, Curtains: blue + * </pre> + * + * @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 ) ); + } +} +?> diff --git a/framework/Log/TEventLog.php b/framework/Log/TEventLog.php new file mode 100644 index 00000000..d291d3f5 --- /dev/null +++ b/framework/Log/TEventLog.php @@ -0,0 +1,56 @@ +<?php
+
+require_once(dirname(__FILE__).'/TLog.php');
+require_once(dirname(__FILE__).'/TEzcLogger.php');
+
+/**
+ * ${classname}
+ *
+ * ${description}
+ *
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package ${package}
+ */
+class TEventLog extends TEzcLogger implements TLog
+{
+
+ public function info($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::INFO,
+ array('source'=>$source, 'category'=>$category));
+ }
+
+ public function debug($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::DEBUG,
+ array('source'=>$source, 'category'=>$category));
+ }
+
+ public function notice($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::NOTICE,
+ array('source'=>$source, 'category'=>$category));
+ }
+
+ public function warn($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::WARNING,
+ array('source'=>$source, 'category'=>$category));
+ }
+
+ public function error($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::NOTICE,
+ array('source'=>$source, 'category'=>$category));
+
+ }
+
+ public function fatal($msg, $source='Prado', $category='main')
+ {
+ ezcLog::getInstance()->log($msg, ezcLog::NOTICE,
+ array('source'=>$source, 'category'=>$category));
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Log/TEzcLogger.php b/framework/Log/TEzcLogger.php new file mode 100644 index 00000000..11fe6c7f --- /dev/null +++ b/framework/Log/TEzcLogger.php @@ -0,0 +1,133 @@ +<?php
+
+abstract class TEzcLogger extends TModule
+{
+ protected $defaultSeverity = 'INFO | DEBUG | NOTICE | WARNING | ERROR | FATAL';
+
+ protected $config;
+
+ public function init($xml)
+ {
+ if(!is_null($this->config))
+ $this->initLogger($this->loadConfigFromFile($this->config));
+ else
+ $this->initLogger($xml);
+ }
+
+ protected function loadConfigFromFile($file)
+ {
+ $xml = new TXmlDocument();
+ $xml->loadFromFile($file);
+ return $xml->getElementByTagName('loggers');
+ }
+
+ protected function initLogger($xml)
+ {
+ TEzcLoggerLoader::using('ezcLog');
+ TEzcLoggerLoader::using('ezcLogMap');
+ TEzcLoggerLoader::using('ezcLogContext');
+ TEzcLoggerLoader::using('ezcLogFilter');
+ $loggers = $xml->getElementsByTagName('logger');
+ $log = ezcLog::getInstance();
+ foreach($loggers as $logger)
+ {
+ $filters = $logger->getElementsByTagName('filter');
+ $logWriter = $this->getLogWriter($logger);
+ foreach($filters as $filter)
+ {
+ $logFilter = new ezcLogFilter();
+ $Severity = $filter->getAttribute('severity');
+ $logFilter->severity = $this->getFilterSeverity($Severity);
+ $map = $filter->getAttribute('disabled') ? 'unmap' : 'map';
+ $log->$map($logFilter, $logWriter);
+ }
+ }
+ }
+
+ protected function getLogWriter($xml)
+ {
+ switch($xml->getAttribute('destination'))
+ {
+ case 'file' :
+ return TEzcLoggerUnixFileWriterFactory::create($xml);
+ default :
+ throw new TException('invalid_log_destination');
+ }
+ }
+
+ protected function getFilterSeverity($string)
+ {
+ if(empty($string))
+ $string = $this->defaultServerity;
+ $serverities = explode("|", $string);
+ $mask = 0;
+ foreach($serverities as $Severity)
+ $mask = $mask | $this->getSeverity($Severity);
+ return $mask;
+ }
+
+ private function getSeverity($string)
+ {
+ switch(strtolower(trim($string)))
+ {
+ case 'debug': return ezcLog::DEBUG;
+ case 'success audit' : return ezcLog::SUCCESS_AUDIT;
+ case 'failed audit' : return ezcLog::FAILED_AUDIT;
+ case 'info' : return ezcLog::INFO;
+ case 'notice' : return ezcLog::NOTICE;
+ case 'warn' : return ezcLog::WARNING;
+ case 'error' : return ezcLog::ERROR;
+ case 'fatal' : return ezcLog::FATAL;
+ }
+ return 0;
+ }
+}
+
+
+/**
+ * ${classname}
+ *
+ * ${description}
+ *
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package ${package}
+ */
+class TEzcLoggerUnixFileWriterFactory
+{
+ public static function create($xml)
+ {
+ TEzcLoggerLoader::using('ezcLogWriter');
+ TEzcLoggerLoader::using('ezcLogWriterFile');
+ TEzcLoggerLoader::using('ezcLogWriterUnixFile');
+
+ $dir = $xml->getAttribute('directory');
+ $file = $xml->getAttribute('filename');
+ if(empty($file)) $file = 'prado.log';
+ return new ezcLogWriterUnixFile($dir, $file);
+ }
+}
+
+/**
+ * ${classname}
+ *
+ * ${description}
+ *
+ * @author Wei Zhuo<weizhuo[at]gmail[dot]com>
+ * @version $Revision: 1.66 $ $Date: ${DATE} ${TIME} $
+ * @package ${package}
+ */
+class TEzcLoggerLoader
+{
+ public static function using($class)
+ {
+ if(class_exists($class, false)) return;
+ static $classes;
+ $base = dirname(__FILE__);
+ if(is_null($classes))
+ $classes = include($base.'/EventLog/log_autoload.php');
+ require_once($base.'/'.$classes[$class]);
+ }
+}
+
+?>
\ No newline at end of file diff --git a/framework/Log/TLog.php b/framework/Log/TLog.php new file mode 100644 index 00000000..0a455c84 --- /dev/null +++ b/framework/Log/TLog.php @@ -0,0 +1,13 @@ +<?php
+
+interface TLog
+{
+ public function info($msg, $source='Prado', $category='main');
+ public function debug($msg, $source='Prado', $category='main');
+ public function notice($msg, $source='Prado', $category='main');
+ public function warn($msg, $source='Prado', $category='main');
+ public function error($msg, $source='Prado', $category='main');
+ public function fatal($msg, $source='Prado', $category='main');
+}
+
+?>
\ No newline at end of file |