This commit deals with several interconnected issues:
authorIvan Habunek <ihabunek@apache.org>
Tue, 28 Feb 2012 07:49:21 +0000 (07:49 +0000)
committerIvan Habunek <ihabunek@apache.org>
Tue, 28 Feb 2012 07:49:21 +0000 (07:49 +0000)
LOG4PHP-172 - Rewritten pattern system to allow longer conversion words & some new ones. Also adapted LoggerAppenderPDO to use the new pattern system.
LOG4PHP-134 - Added reconnectAttempts parameter to LoggerAppenderPDO. Enables reconnect attempts if appending fails.
LOG4PHP-163 - Fixed formatting bug in LoggerLayoutPattern

git-svn-id: https://svn.apache.org/repos/asf/logging/log4php/trunk@1294503 13f79535-47bb-0310-9956-ffa450edef68

45 files changed:
src/changes/changes.xml
src/main/php/LoggerAutoloader.php
src/main/php/LoggerLoggingEvent.php
src/main/php/appenders/LoggerAppenderPDO.php
src/main/php/helpers/LoggerBasicPatternConverter.php [deleted file]
src/main/php/helpers/LoggerFormattingInfo.php
src/main/php/helpers/LoggerLocationPatternConverter.php [deleted file]
src/main/php/helpers/LoggerNamedPatternConverter.php [deleted file]
src/main/php/helpers/LoggerPatternConverter.php [deleted file]
src/main/php/helpers/LoggerPatternParser.php
src/main/php/helpers/LoggerUtils.php [new file with mode: 0644]
src/main/php/layouts/LoggerLayoutPattern.php
src/main/php/pattern/LoggerPatternConverter.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterClass.php [moved from src/main/php/helpers/LoggerMDCPatternConverter.php with 64% similarity]
src/main/php/pattern/LoggerPatternConverterCookie.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterDate.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterEnvironment.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterFile.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterLevel.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterLine.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterLiteral.php [moved from src/main/php/helpers/LoggerCategoryPatternConverter.php with 65% similarity]
src/main/php/pattern/LoggerPatternConverterLocation.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterLogger.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterMDC.php [moved from src/main/php/helpers/LoggerDatePatternConverter.php with 55% similarity]
src/main/php/pattern/LoggerPatternConverterMessage.php [moved from src/main/php/helpers/LoggerLiteralPatternConverter.php with 62% similarity]
src/main/php/pattern/LoggerPatternConverterMethod.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterNDC.php [moved from src/main/php/helpers/LoggerClassNamePatternConverter.php with 64% similarity]
src/main/php/pattern/LoggerPatternConverterNewLine.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterProcess.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterRelative.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterRequest.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterServer.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterSession.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterSessionID.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterSuperglobal.php [new file with mode: 0644]
src/main/php/pattern/LoggerPatternConverterThrowable.php [new file with mode: 0644]
src/site/xdoc/docs/layouts/pattern.xml
src/test/php/LoggerMDCTest.php
src/test/php/LoggerTestHelper.php
src/test/php/appenders/LoggerAppenderPDOTest.php
src/test/php/bootstrap.php
src/test/php/helpers/LoggerPatternParserTest.php
src/test/php/helpers/LoggerUtilsTest.php [new file with mode: 0644]
src/test/php/layouts/LoggerLayoutPatternTest.php
src/test/php/pattern/LoggerPatternConverterTest.php [new file with mode: 0644]

index b4338c6..73ee71b 100644 (file)
                <title>Apache log4php changelog</title>
        </properties>
        <body>
-           <release version="2.2.1" date="2012-02-18.">
+               <release version="2.3.0" date="SVN">
+                       <action date="2011-02-28" type="fix" issue="LOG4PHP-163" dev="Ivan Habunek">Fixed formatting bug in LoggerLayoutPattern.</action>
+                       <action date="2011-02-28" type="update" issue="LOG4PHP-172" dev="Ivan Habunek">Rewritten pattern system to allow longer conversion words and some new ones.</action>
+                       <action date="2011-02-28" type="add" issue="LOG4PHP-134" dev="Ivan Habunek">Added reconnectAttempts parameter to LoggerAppenderPDO. Adds support for reconnect if connection fails while appending.</action>
+               </release>
+           <release version="2.2.1" date="2012-02-18">
                <action date="2011-02-07" type="fix" issue="LOG4PHP-168" dev="Ivan Habunek">Fixed a bug which prevented configuration by passing a LoggerConfigurator instance.</action>
                <action date="2012-01-29" type="fix" issue="LOG4PHP-167" dev="Ivan Habunek">Fixed a bug which prevented parsing of INI configuration files when using PHP 5.2.x.</action>
                <action date="2011-12-22" type="update" issue="LOG4PHP-166" dev="Ivan Habunek" due-to="David Hilowitz" due-to-email="dhilowitz at gmail dot com">Added connection timeout parameter to MongoDB appender.</action>
index 225757c..1cfae1f 100644 (file)
@@ -42,6 +42,7 @@ class LoggerAutoloader {
                'LoggerConfigurable' => '/LoggerConfigurable.php',\r
                'LoggerConfigurator' => '/LoggerConfigurator.php',\r
                'LoggerException' => '/LoggerException.php',\r
+               'LoggerFilter' => '/LoggerFilter.php',\r
                'LoggerHierarchy' => '/LoggerHierarchy.php',\r
                'LoggerLevel' => '/LoggerLevel.php',\r
                'LoggerLocationInfo' => '/LoggerLocationInfo.php',\r
@@ -52,7 +53,7 @@ class LoggerAutoloader {
                'LoggerReflectionUtils' => '/LoggerReflectionUtils.php',\r
                'LoggerRoot' => '/LoggerRoot.php',\r
                'LoggerThrowableInformation' => '/LoggerThrowableInformation.php',\r
-\r
+               \r
                // Appenders\r
                'LoggerAppenderConsole' => '/appenders/LoggerAppenderConsole.php',\r
                'LoggerAppenderDailyFile' => '/appenders/LoggerAppenderDailyFile.php',\r
@@ -76,7 +77,6 @@ class LoggerAutoloader {
                'LoggerConfiguratorDefault' => '/configurators/LoggerConfiguratorDefault.php',\r
 \r
                // Filters\r
-               'LoggerFilter' => '/LoggerFilter.php',\r
                'LoggerFilterDenyAll' => '/filters/LoggerFilterDenyAll.php',\r
                'LoggerFilterLevelMatch' => '/filters/LoggerFilterLevelMatch.php',\r
                'LoggerFilterLevelRange' => '/filters/LoggerFilterLevelRange.php',\r
@@ -86,18 +86,33 @@ class LoggerAutoloader {
                'LoggerFormattingInfo' => '/helpers/LoggerFormattingInfo.php',\r
                'LoggerOptionConverter' => '/helpers/LoggerOptionConverter.php',\r
                'LoggerPatternParser' => '/helpers/LoggerPatternParser.php',\r
+               'LoggerUtils' => '/helpers/LoggerUtils.php',\r
+       \r
+               // Pattern converters\r
+               'LoggerPatternConverter' => '/pattern/LoggerPatternConverter.php',\r
+               'LoggerPatternConverterClass' => '/pattern/LoggerPatternConverterClass.php',\r
+               'LoggerPatternConverterCookie' => '/pattern/LoggerPatternConverterCookie.php',\r
+               'LoggerPatternConverterDate' => '/pattern/LoggerPatternConverterDate.php',\r
+               'LoggerPatternConverterEnvironment' => '/pattern/LoggerPatternConverterEnvironment.php',\r
+               'LoggerPatternConverterFile' => '/pattern/LoggerPatternConverterFile.php',\r
+               'LoggerPatternConverterLevel' => '/pattern/LoggerPatternConverterLevel.php',\r
+               'LoggerPatternConverterLine' => '/pattern/LoggerPatternConverterLine.php',\r
+               'LoggerPatternConverterLiteral' => '/pattern/LoggerPatternConverterLiteral.php',\r
+               'LoggerPatternConverterLogger' => '/pattern/LoggerPatternConverterLogger.php',\r
+               'LoggerPatternConverterMDC' => '/pattern/LoggerPatternConverterMDC.php',\r
+               'LoggerPatternConverterMessage' => '/pattern/LoggerPatternConverterMessage.php',\r
+               'LoggerPatternConverterMethod' => '/pattern/LoggerPatternConverterMethod.php',\r
+               'LoggerPatternConverterNDC' => '/pattern/LoggerPatternConverterNDC.php',\r
+               'LoggerPatternConverterNewLine' => '/pattern/LoggerPatternConverterNewLine.php',\r
+               'LoggerPatternConverterProcess' => '/pattern/LoggerPatternConverterProcess.php',\r
+               'LoggerPatternConverterRelative' => '/pattern/LoggerPatternConverterRelative.php',\r
+               'LoggerPatternConverterRequest' => '/pattern/LoggerPatternConverterRequest.php',\r
+               'LoggerPatternConverterServer' => '/pattern/LoggerPatternConverterServer.php',\r
+               'LoggerPatternConverterSession' => '/pattern/LoggerPatternConverterSession.php',\r
+               'LoggerPatternConverterSessionID' => '/pattern/LoggerPatternConverterSessionID.php',\r
+               'LoggerPatternConverterSuperglobal' => '/pattern/LoggerPatternConverterSuperglobal.php',\r
+               'LoggerPatternConverterThrowable' => '/pattern/LoggerPatternConverterThrowable.php',\r
                \r
-               // Converters\r
-               'LoggerBasicPatternConverter' => '/helpers/LoggerBasicPatternConverter.php',\r
-               'LoggerCategoryPatternConverter' => '/helpers/LoggerCategoryPatternConverter.php',\r
-               'LoggerClassNamePatternConverter' => '/helpers/LoggerClassNamePatternConverter.php',\r
-               'LoggerDatePatternConverter' => '/helpers/LoggerDatePatternConverter.php',\r
-               'LoggerLiteralPatternConverter' => '/helpers/LoggerLiteralPatternConverter.php',\r
-               'LoggerLocationPatternConverter' => '/helpers/LoggerLocationPatternConverter.php',\r
-               'LoggerMDCPatternConverter' => '/helpers/LoggerMDCPatternConverter.php',\r
-               'LoggerNamedPatternConverter' => '/helpers/LoggerNamedPatternConverter.php',\r
-               'LoggerPatternConverter' => '/helpers/LoggerPatternConverter.php',\r
-\r
                // Layouts\r
                'LoggerLayoutHtml' => '/layouts/LoggerLayoutHtml.php',\r
                'LoggerLayoutPattern' => '/layouts/LoggerLayoutPattern.php',\r
@@ -112,7 +127,7 @@ class LoggerAutoloader {
                'LoggerRendererMap' => '/renderers/LoggerRendererMap.php',\r
                'LoggerRendererObject' => '/renderers/LoggerRendererObject.php',\r
        );\r
-\r
+       \r
        /**\r
         * Loads a class.\r
         * @param string $className The name of the class to load.\r
index 3feae3a..05c4c6c 100644 (file)
@@ -212,6 +212,14 @@ class LoggerLoggingEvent {
        }
 
        /**
+        * Returns the logger which created the event.
+        * @return Logger
+        */
+       public function getLogger() {
+               return $this->logger;
+       }
+       
+       /**
         * Return the name of the logger. Use this form instead of directly
         * accessing the {@link $categoryName} field.
         * @return string  
index 8bd27b8..c93fe1a 100644 (file)
  *
  * Configurable parameters of this appender are:
  *
- * - user            - Sets the user of this database connection
- * - password        - Sets the password of this database connection
- * - createTable     - true, if the table should be created if necessary. false otherwise
- * - table           - Sets the table name (default: log4php_log)
- * - sql             - Sets the insert statement for a logging event. Defaults
+ * - dsn             - The DSN string for this connection
+ * - user            - The user of this database connection
+ * - password        - The password of this database connection
+ * - table           - Name of the database table into which log entries will be inserted (default: log4php_log)
+ * - insertSQL       - Sets the insert statement for a logging event. Defaults
  *                     to the correct one - change only if you are sure what you are doing.
- * - dsn             - Sets the DSN string for this connection
+ * - insertPattern   - The conversion pattern to use in conjuction with insert 
+ *                     SQL. Must contain the same number of comma separated 
+ *                     conversion patterns as there are question marks in the 
+ *                     insertSQL.
  *
- * If $sql is set then $table and $sql are used, else $table, $insertSql and $insertPattern.
+ * If $sql is set then $table and $sql are used, else $table, $insertSQL and $insertPattern.
  *
  * An example:
  *
  */
 class LoggerAppenderPDO extends LoggerAppender {
 
-       /** 
-        * Create the log table if it does not exists (optional).
-        * @var string 
-        */
-       protected $createTable = true;
+       // ******************************************
+       // *** Configurable parameters            ***
+       // ******************************************
        
        /** 
-        * Database user name.
-        * @var string 
+        * DSN string used to connect to the database.
+        * @see http://www.php.net/manual/en/pdo.construct.php
         */
+       protected $dsn;
+
+       /** Database user name. */
        protected $user;
        
-       /** 
-        * Database password
-        * @var string 
-        */
+       /** Database password. */
        protected $password;
        
        /** 
-        * DSN string for enabling a connection.
-        * @var string 
-        */
-       protected $dsn;
-       
-       /** 
-        * A {@link LoggerPatternLayout} string used to format a valid insert query.
-        * @deprecated Use {@link $insertSql} and {@link $insertPattern} which properly handle quotes in the messages!
-        * @var string 
-        */
-       protected $sql;
-       
-       /** 
-        * Can be set to a complete insert statement with ? that are replaced using {@link insertPattern}.
-        * @var string 
+        * The insert query.
+        * 
+        * The __TABLE__ placeholder will be replaced by the table name from 
+        * {@link $table}.
+        *  
+        * The questionmarks are part of the prepared statement, and they must 
+        * match the number of conversion specifiers in {@link insertPattern}.
         */
-       protected $insertSql = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?,?,?,?,?,?,?)";
+       protected $insertSQL = "INSERT INTO __TABLE__ (timestamp, logger, level, message, thread, file, line) VALUES (?, ?, ?, ?, ?, ?, ?)";
 
        /** 
-        * A comma separated list of {@link LoggerPatternLayout} format strings that replace the "?" in {@link $sql}.
-        * @var string 
+        * A comma separated list of {@link LoggerPatternLayout} format strings 
+        * which replace the "?" in {@link $insertSQL}.
+        * 
+        * Must contain the same number of comma separated conversion patterns as 
+        * there are question marks in {@link insertSQL}.
+        * 
+        * @see LoggerPatternLayout For conversion patterns.
         */
-       protected $insertPattern = "%d,%c,%p,%m,%t,%F,%L";
+       protected $insertPattern = "%date{Y-m-d H:i:s},%logger,%level,%message,%pid,%file,%line";
 
-       /** 
-        * Table name to write events. Used only for CREATE TABLE if {@link $createTable} is true.
-        * @var string 
-        */
+       /** Name of the table to which to append log events. */
        protected $table = 'log4php_log';
        
+       /** The number of recconect attempts to make on failed append. */
+       protected $reconnectAttempts = 3;
+       
+       
+       // ******************************************
+       // *** Private memebers                   ***
+       // ******************************************
+       
        /** 
         * The PDO instance.
         * @var PDO 
         */
-       protected $db = null;
+       protected $db;
        
        /** 
-        * Prepared statement for the INSERT INTO query.
+        * Prepared statement for the insert query.
         * @var PDOStatement 
         */
        protected $preparedInsert;
-
-       /** 
-        * Set in activateOptions() and later used in append() to check if all conditions to append are true.
-        * @var boolean 
-        */
-       protected $canAppend = true;
        
-       /**
-        * This appender does not require a layout.
-        */
+       /** This appender does not require a layout. */
        protected $requiresLayout = false;
        
+
+       // ******************************************
+       // *** Appender methods                   ***
+       // ******************************************
+       
        /**
-        * Setup db connection.
-        * Based on defined options, this method connects to db defined in {@link $dsn}
-        * and creates a {@link $table} table if {@link $createTable} is true.
-        * @return boolean true if all ok.
-        * @throws a PDOException if the attempt to connect to the requested database fails.
+        * Acquires a database connection based on parameters.
+        * Parses the insert pattern to create a chain of converters which will be
+        * used in forming query parameters from logging events.
         */
        public function activateOptions() {
                try {
-                       if($this->user === null) {
-                               $this->db = new PDO($this->dsn);
-                       } else if($this->password === null) {
-                               $this->db = new PDO($this->dsn, $this->user);
-                       } else {
-                               $this->db = new PDO($this->dsn,$this->user,$this->password);
-                       }
-                       $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-                       
-                       // test if log table exists
-                       try {
-                               $result = $this->db->query('SELECT * FROM ' . $this->table . ' WHERE 1 = 0');
-                               $result->closeCursor(); 
-                       } catch (PDOException $e) {
-                               // It could be something else but a "no such table" is the most likely
-                               $result = false;
-                       }
-                       
-                       // create table if necessary
-                       if ($result == false and $this->createTable) {
-                               // The syntax should at least be compatible with MySQL, PostgreSQL, SQLite and Oracle.
-                               $query = "CREATE TABLE {$this->table} (".
-                                                       "timestamp varchar(32)," .
-                                                       "logger varchar(64)," .
-                                                       "level varchar(32)," .
-                                                       "message varchar(9999)," .
-                                                       "thread varchar(32)," .
-                                                       "file varchar(255)," .
-                                                       "line varchar(6))";
-                               $result = $this->db->query($query);
-                       }
+                       $this->establishConnection();
                } catch (PDOException $e) {
-                       $this->canAppend = false;
-                       throw new LoggerException($e);
+                       $this->warn("Failed connecting to database: " . $e->getMessage());
+                       $this->close();
+                       return;
+               }
+
+               // Parse the insert patterns; pattern parts are comma delimited
+               $pieces = explode(',', $this->insertPattern);
+               $converterMap = LoggerLayoutPattern::getDefaultConverterMap();
+               foreach($pieces as $pattern) {
+                       $parser = new LoggerPatternParser($pattern, $converterMap);
+                       $this->converters[] = $parser->parse(); 
                }
                
-               $this->layout = new LoggerLayoutPattern();
+               $this->closed = false;
+       }
+       
+       /** 
+        * Connects to the database, and prepares the insert query.
+        * @throws PDOException If connect or prepare fails.  
+        */
+       protected function establishConnection() {
+               // Acquire database connection
+               $this->db = new PDO($this->dsn, $this->user, $this->password);
+               $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
                
-               //
-               // Keep compatibility to legacy option $sql which already included the format patterns!
-               //
-               if (empty($this->sql)) {
-                       // new style with prepared Statment and $insertSql and $insertPattern
-                       // Maybe the tablename has to be substituted.
-                       $this->insertSql = preg_replace('/__TABLE__/', $this->table, $this->insertSql);
-                       $this->preparedInsert = $this->db->prepare($this->insertSql);
-                       $this->layout->setConversionPattern($this->insertPattern);
-               } else {
-                       // Old style with format strings in the $sql query should be used.
-               $this->layout->setConversionPattern($this->sql);
-               }
-
-               $this->canAppend = true;
-               return true;
+               // Prepare the insert statement
+               $insertSQL = str_replace('__TABLE__', $this->table, $this->insertSQL);
+               $this->preparedInsert = $this->db->prepare($insertSQL);
        }
        
        /**
         * Appends a new event to the database.
         * 
-        * @throws LoggerException If the pattern conversion or the INSERT statement fails.
+        * If writing to database fails, it will retry by re-establishing the 
+        * connection up to $reconnectAttempts times. If writing still fails, 
+        * the appender will close.
         */
        public function append(LoggerLoggingEvent $event) {
-               // TODO: Can't activateOptions() simply throw an Exception if it encounters problems?
-               if ( ! $this->canAppend) return;
-
+               
+               for ($attempt = 1; $attempt <= $this->reconnectAttempts; $attempt++) {
                        try {
-                       if (empty($this->sql)) {
-                               // new style with prepared statement
-                               $params = $this->layout->formatToArray($event);
-                               $this->preparedInsert->execute($params);
-                       } else {
-                               // old style
-                               $query = $this->layout->format($event);
-                               $this->db->exec($query);
+                               // If it's a retry, reestablish the connection
+                               if ($attempt > 1) {
+                                       $this->establishConnection();
+                               }
+                               
+                               // Write to database
+                               @$this->preparedInsert->execute($this->format($event));
+                               @$this->preparedInsert->closeCursor();
+                               break;
+                       } catch (PDOException $e) {
+                               $this->warn("Failed writing to database: ". $e->getMessage());
+                               
+                               // Close the appender if it's the last attempt
+                               if ($attempt == $this->reconnectAttempts) {
+                                       $this->warn("Failed after {$this->reconnectAttempts} attempts. Closing appender.");
+                                       $this->close();
+                               } else {
+                                       $this->warn("Attempting a reconnect (attempt $attempt of {$this->reconnectAttempts}).");
+                               }
                        }
-                       } catch (Exception $e) {
-                               throw new LoggerException($e);
+               }
+       }
+       
+       /**
+        * Converts the logging event to a series of database parameters by using 
+        * the converter chain which was set up on activation. 
+        */
+       protected function format(LoggerLoggingEvent $event) {
+               $params = array();
+               foreach($this->converters as $converter) {
+                       $buffer = '';
+                       while ($converter !== null) {
+                               $converter->format($buffer, $event);
+                               $converter = $converter->next;
                        }
+                       $params[] = $buffer;
                }
+               return $params;
+       }
        
        /**
         * Closes the connection to the logging database
         */
        public function close() {
-               if($this->closed != true) {
-                       if ($this->db !== null) {
-                               $this->db = null;
-                       }
-                       $this->closed = true;
-               }
+               // Close the connection (if any)
+               $this->db = null;
+               
+               // Close the appender
+               $this->closed = true;
        }
        
+       // ******************************************
+       // *** Accessor methods                   ***
+       // ******************************************
+       
        /**
-        * Sets the username for this connection. 
-        * Defaults to ''
+        * Returns the active database handle or null if not established.
+        * @return PDO
         */
+       public function getDatabaseHandle() {
+               return $this->db;
+       }
+       
+       /** Sets the username. */
        public function setUser($user) {
                $this->setString('user', $user);
        }
        
-       /**
-        * Sets the password for this connection. 
-        * Defaults to ''
-        */
+       /** Returns the username. */
+       public function getUser($user) {
+               return $this->user;
+       }
+       
+       /** Sets the password. */
        public function setPassword($password) {
                $this->setString('password', $password);
        }
        
-       /**
-        * Indicator if the logging table should be created on startup,
-        * if its not existing.
-        */
-       public function setCreateTable($flag) {
-               $this->setBoolean('createTable', $flag);
+       /** Returns the password. */
+       public function getPassword($password) {
+               return $this->password;
        }
-   
-       /**
-        * Sets the SQL string into which the event should be transformed.
-        * Defaults to:
-        * 
-        * INSERT INTO $this->table 
-        * ( timestamp, logger, level, message, thread, file, line) 
-        * VALUES 
-        * ('%d','%c','%p','%m','%t','%F','%L')
-        * 
-        * It's not necessary to change this except you have customized logging'
-        *
-        * @deprecated See {@link setInsertSql} and {@link setInsertPattern}.
-        */
-       public function setSql($sql) {
-               $this->setString('sql', $sql);
+       
+       /** Sets the insert SQL. */
+       public function setInsertSQL($sql) {
+               $this->setString('insertSQL', $sql);
        }
        
-       /**
-        * Sets the SQL INSERT string to use with {@link $insertPattern}.
-        *
-        * @param $sql            A complete INSERT INTO query with "?" that gets replaced.
-        */
-       public function setInsertSql($sql) {
-               $this->setString('insertSql', $sql);
+       /** Returns the insert SQL. */
+       public function getInsertSQL($sql) {
+               return $this->insertSQL;
        }
 
-       /**
-        * Sets the {@link LoggerLayoutPattern} format strings for {@link $insertSql}.
-        *
-        * It's not necessary to change this except you have customized logging.
-        *
-        * @param $pattern                Comma separated format strings like "%p,%m,%C"
-        */
+       /** Sets the insert pattern. */
        public function setInsertPattern($pattern) {
                $this->setString('insertPattern', $pattern);
        }
+       
+       /** Returns the insert pattern. */
+       public function getInsertPattern($pattern) {
+               return $this->insertPattern;
+       }
 
-       /**
-        * Sets the tablename to which this appender should log.
-        * Defaults to log4php_log
-        */
+       /** Sets the table name. */
        public function setTable($table) {
                $this->setString('table', $table);
        }
        
-       /**
-        * Sets the DSN string for this connection. In case of
-        * SQLite it could look like this: 'sqlite:appenders/pdotest.sqlite'
-        */
+       /** Returns the table name. */
+       public function getTable($table) {
+               return $this->table;
+       }
+       
+       /** Sets the DSN string. */
        public function setDSN($dsn) {
                $this->setString('dsn', $dsn);
        }
        
-       /**
-        * Sometimes databases allow only one connection to themselves in one thread.
-        * SQLite has this behaviour. In that case this handle is needed if the database
-        * must be checked for events.
-        *
-        * @return PDO
-        */
-       public function getDatabaseHandle() {
-               return $this->db;
-       }
+       /** Returns the DSN string. */
+       public function getDSN($dsn) {
+               return $this->setString('dsn', $dsn);
+       }       
 }
 
diff --git a/src/main/php/helpers/LoggerBasicPatternConverter.php b/src/main/php/helpers/LoggerBasicPatternConverter.php
deleted file mode 100644 (file)
index 18b8c91..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerBasicPatternConverter extends LoggerPatternConverter {
-
-       /**
-        * @var integer
-        */
-       private $type;
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param integer $type
-        */
-       public function __construct($formattingInfo, $type) {
-               parent::__construct($formattingInfo);
-               $this->type = $type;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function convert($event) {
-               switch($this->type) {
-                       case LoggerPatternParser::RELATIVE_TIME_CONVERTER:
-                               $timeStamp = $event->getTimeStamp();
-                               $startTime = LoggerLoggingEvent::getStartTime();
-                               return (string)(int)($timeStamp * 1000 - $startTime * 1000);
-                               
-                       case LoggerPatternParser::THREAD_CONVERTER:
-                               return $event->getThreadName();
-
-                       case LoggerPatternParser::LEVEL_CONVERTER:
-                               $level = $event->getLevel();
-                               return $level->toString();
-
-                       case LoggerPatternParser::NDC_CONVERTER:
-                               return $event->getNDC();
-
-                       case LoggerPatternParser::MESSAGE_CONVERTER:
-                               return $event->getRenderedMessage();
-                               
-                       default: 
-                               return '';
-               }
-       }
-}
index 9ebcc7e..a8a54a6 100644 (file)
  * @since 0.3
  */
 class LoggerFormattingInfo {
-
-       public $min = -1;
-       public $max = 0x7FFFFFFF;
-       public $leftAlign = false;
-
+       
+       /** 
+        * Minimal output length. If output is shorter than this value, it will be
+        * padded with spaces. 
+        */
+       public $min = 0;
+       
+       /** 
+        * Maximum output length. If output is longer than this value, it will be 
+        * trimmed.
+        */
+       public $max = PHP_INT_MAX;
+       
        /**
-        * Constructor
+        * Whether to pad the string from the left. If set to false, the string 
+        * will be padded from the right. 
         */
-       public function __construct() {}
+       public $padLeft = true;
        
-       public function reset() {
-               $this->min = -1;
-               $this->max = 0x7FFFFFFF;
-               $this->leftAlign = false;
-       }
-
-       public function dump() {
-               // TODO: other option to dump?
-               // LoggerLog::debug("LoggerFormattingInfo::dump() min={$this->min}, max={$this->max}, leftAlign={$this->leftAlign}");
-       }
+       /**
+        * Whether to trim the string from the left. If set to false, the string
+        * will be trimmed from the right.
+        */
+       public $trimLeft = false;
 }
diff --git a/src/main/php/helpers/LoggerLocationPatternConverter.php b/src/main/php/helpers/LoggerLocationPatternConverter.php
deleted file mode 100644 (file)
index 5d78c92..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerLocationPatternConverter extends LoggerPatternConverter {
-       
-       /**
-        * @var integer
-        */
-       private $type;
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param integer $type
-        */
-       public function __construct($formattingInfo, $type) {
-               parent::__construct($formattingInfo);
-               $this->type = $type;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function convert($event) {
-               $locationInfo = $event->getLocationInformation();
-               switch($this->type) {
-                       case LoggerPatternParser::FULL_LOCATION_CONVERTER:
-                               return $locationInfo->getFullInfo();
-                       case LoggerPatternParser::METHOD_LOCATION_CONVERTER:
-                               return $locationInfo->getMethodName();
-                       case LoggerPatternParser::LINE_LOCATION_CONVERTER:
-                               return $locationInfo->getLineNumber();
-                       case LoggerPatternParser::FILE_LOCATION_CONVERTER:
-                               return $locationInfo->getFileName();
-                       case LoggerPatternParser::CLASS_LOCATION_CONVERTER:
-                               return $locationInfo->getFullQualifiedClassname();
-                       default: 
-                               return '';
-               }
-       }
-}
-
diff --git a/src/main/php/helpers/LoggerNamedPatternConverter.php b/src/main/php/helpers/LoggerNamedPatternConverter.php
deleted file mode 100644 (file)
index eda3326..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerNamedPatternConverter extends LoggerPatternConverter {
-
-       /**
-        * @var integer
-        */
-       private $precision;
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param integer $precision
-        */
-       public function __construct($formattingInfo, $precision) {
-               parent::__construct($formattingInfo);
-               $this->precision = $precision;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function getFullyQualifiedName($event) {
-               return;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       function convert($event) {
-               $n = $this->getFullyQualifiedName($event);
-               if($this->precision <= 0) {
-                       return $n;
-               } else {
-                       
-                       // TODO: do this with explode()
-                       
-                       $len = strlen($n);
-                       // We substract 1 from 'len' when assigning to 'end' to avoid out of
-                       // bounds exception in return r.substring(end+1, len). This can happen if
-                       // precision is 1 and the category name ends with a dot.
-                       $end = $len -1 ;
-                       for($i = $this->precision; $i > 0; $i--) {
-                               $end = strrpos(substr($n, 0, ($end - 1)), '.');
-                               if($end == false) {
-                                       return $n;
-                               }
-                       }
-                       return substr($n, ($end + 1), $len);
-               }
-       }
-}
diff --git a/src/main/php/helpers/LoggerPatternConverter.php b/src/main/php/helpers/LoggerPatternConverter.php
deleted file mode 100644 (file)
index d145997..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-
-/**
- * LoggerPatternConverter is an abstract class that provides the formatting 
- * functionality that derived classes need.
- * 
- * <p>Conversion specifiers in a conversion patterns are parsed to
- * individual PatternConverters. Each of which is responsible for
- * converting a logging event in a converter specific manner.</p>
- * 
- * @version $Revision$
- * @package log4php
- * @subpackage helpers
- * @since 0.3
- */
-class LoggerPatternConverter {
-       
-       /**
-        * Array for fast space padding
-        * Used by {@link LoggerPatternConverter::spacePad()}.  
-        */
-       private $spaces = array(
-               " ", // 1 space
-               "  ", // 2 spaces
-               "    ", // 4 spaces
-               "        ", // 8 spaces
-               "                ", // 16 spaces
-               "                                "); // 32 spaces 
-        
-       /**
-        * @var LoggerPatternConverter next converter in converter chain
-        */
-       public $next = null;
-       
-       public $min = -1;
-       public $max = 0x7FFFFFFF;
-       public $leftAlign = false;
-
-       /**
-        * Constructor 
-        *
-        * @param LoggerFormattingInfo $fi
-        */
-       public function __construct($fi = null) {  
-               if($fi !== null) {
-                       $this->min = $fi->min;
-                       $this->max = $fi->max;
-                       $this->leftAlign = $fi->leftAlign;
-               }
-       }
-  
-       /**
-        * Derived pattern converters must override this method in order to
-        * convert conversion specifiers in the correct way.
-        *
-        * @param LoggerLoggingEvent $event
-        */
-       public function convert($event) {}
-
-       /**
-        * A template method for formatting in a converter specific way.
-        *
-        * @param string &$sbuf string buffer
-        * @param LoggerLoggingEvent $e
-        */
-       public function format(&$sbuf, $e) {
-               $s = $this->convert($e);
-               
-               if($s == null or empty($s)) {
-                       if(0 < $this->min) {
-                               $this->spacePad($sbuf, $this->min);
-                       }
-                       return;
-               }
-               
-               $len = strlen($s);
-       
-               if($len > $this->max) {
-                       $sbuf .= substr($s , 0, ($len - $this->max));
-               } else if($len < $this->min) {
-                       if($this->leftAlign) {          
-                               $sbuf .= $s;
-                               $this->spacePad($sbuf, ($this->min - $len));
-                       } else {
-                               $this->spacePad($sbuf, ($this->min - $len));
-                               $sbuf .= $s;
-                       }
-               } else {
-                       $sbuf .= $s;
-               }
-       }       
-
-       /**
-        * Fast space padding method.
-        *
-        * @param string        &$sbuf     string buffer
-        * @param integer       $length    pad length
-        *
-        * @todo reimplement using PHP string functions
-        */
-       public function spacePad(&$sbuf, $length) {
-               while($length >= 32) {
-                 $sbuf .= $this->spaces[5];
-                 $length -= 32;
-               }
-               
-               for($i = 4; $i >= 0; $i--) {    
-                       if(($length & (1<<$i)) != 0) {
-                               $sbuf .= $this->spaces[$i];
-                       }
-               }
-
-               // $sbuf = str_pad($sbuf, $length);
-       }
-}
index 9d65606..79dc140 100644 (file)
  */
 class LoggerPatternParser {
 
+       /** Escape character for conversion words in the conversion pattern. */
        const ESCAPE_CHAR = '%';
        
-       const LITERAL_STATE = 0;
-       const CONVERTER_STATE = 1;
-       const MINUS_STATE = 2;
-       const DOT_STATE = 3;
-       const MIN_STATE = 4;
-       const MAX_STATE = 5;
+       /** Maps conversion words to relevant converters. */
+       private $converterMap;
        
-       const FULL_LOCATION_CONVERTER = 1000;
-       const METHOD_LOCATION_CONVERTER = 1001;
-       const CLASS_LOCATION_CONVERTER = 1002;
-       const FILE_LOCATION_CONVERTER = 1003;
-       const LINE_LOCATION_CONVERTER = 1004;
-       
-       const RELATIVE_TIME_CONVERTER = 2000;
-       const THREAD_CONVERTER = 2001;
-       const LEVEL_CONVERTER = 2002;
-       const NDC_CONVERTER = 2003;
-       const MESSAGE_CONVERTER = 2004;
+       /** Conversion pattern used in layout. */
+       private $pattern;
        
-       const DATE_FORMAT_ISO8601 = 'Y-m-d H:i:s,u'; 
-       const DATE_FORMAT_ABSOLUTE = 'H:i:s';
-       const DATE_FORMAT_DATE = 'd M Y H:i:s,u';
-
-       private $state;
-       private $currentLiteral;
-       private $patternLength;
-       private $i;
+       /** Regex pattern used for parsing the conversion pattern. */
+       private $regex;
        
-       /**
+       /** 
+        * First converter in the chain. 
         * @var LoggerPatternConverter
         */
-       private $head = null;
-        
-       /**
-        * @var LoggerPatternConverter
-        */
-       private $tail = null;
+       private $head;
        
-       /**
-        * @var LoggerFormattingInfo
-        */
-       private $formattingInfo;
+       /** Last converter in the chain. */
+       private $tail;
        
-       /**
-        * @var string pattern to parse
+       public function __construct($pattern, $converterMap) {
+               $this->pattern = $pattern;
+               $this->converterMap = $converterMap;
+               
+               // Construct the regex pattern
+               $this->regex = 
+                       '/' .                       // Starting regex pattern delimiter
+                       self::ESCAPE_CHAR .         // Character which marks the start of the conversion pattern
+                       '(?P<modifiers>[0-9.-]*)' . // Format modifiers (optional)
+                       '(?P<word>[a-zA-Z]+)' .     // The conversion word
+                       '(?P<option>{[^}]*})?' .   // Conversion option in braces (optional)
+                       '/';                        // Ending regex pattern delimiter
+       }
+       
+       /** 
+        * Parses the conversion pattern string, converts it to a chain of pattern
+        * converters and returns the first converter in the chain.
+        * 
+        * @return LoggerPatternConverter
         */
-       private $pattern;
+       public function parse() {
+               
+               // Skip parsing if the pattern is empty
+               if (empty($this->pattern)) {
+                       $this->addLiteral('');
+                       return $this->head;
+               }
+               
+               // Find all conversion words in the conversion pattern
+               $count = preg_match_all($this->regex, $this->pattern, $matches, PREG_OFFSET_CAPTURE);
+               if ($count === false) {
+                       $error = error_get_last();
+                       throw new LoggerException("Failed parsing layotut pattern: {$error['message']}");
+               }
+               
+               $prevEnd = 0;
+               
+               foreach($matches[0] as $key => $item) {
+                       
+                       // Locate where the conversion command starts and ends
+                       $length = strlen($item[0]);
+                       $start = $item[1];
+                       $end = $item[1] + $length;
+               
+                       // Find any literal expressions between matched commands
+                       if ($start > $prevEnd) {
+                               $literal = substr($this->pattern, $prevEnd, $start - $prevEnd);
+                               $this->addLiteral($literal);
+                       }
+                       
+                       // Extract the data from the matched command
+                       $word = !empty($matches['word'][$key]) ? $matches['word'][$key][0] : null;
+                       $modifiers = !empty($matches['modifiers'][$key]) ? $matches['modifiers'][$key][0] : null;
+                       $option = !empty($matches['option'][$key]) ? $matches['option'][$key][0] : null;
+                       
+                       // Create a converter and add it to the chain
+                       $this->addConverter($word, $modifiers, $option);
+                       
+                       $prevEnd = $end;
+               }
 
-       /**
-        * Constructor 
-        *
-        * @param string $pattern
+               // Add any trailing literals
+               if ($end < strlen($this->pattern)) {
+                       $literal = substr($this->pattern, $end);
+                       $this->addLiteral($literal);
+               }
+               
+               return $this->head;
+       }
+       
+       /** 
+        * Adds a literal converter to the converter chain. 
+        * @param string $string The string for the literal converter.
         */
-       public function __construct($pattern) {
-               $this->pattern = $pattern;
-               $this->patternLength =  strlen($pattern);
-               $this->formattingInfo = new LoggerFormattingInfo();
-               $this->state = self::LITERAL_STATE;
+       private function addLiteral($string) {
+               $converter = new LoggerPatternConverterLiteral($string);
+               $this->addToChain($converter);
        }
-
+       
        /**
-        * @param LoggerPatternConverter $pc
+        * Adds a non-literal converter to the converter chain.
+        * 
+        * @param string $word The conversion word, used to determine which 
+        *  converter will be used.
+        * @param string $modifiers Formatting modifiers.
+        * @param string $option Option to pass to the converter.
         */
-       public function addToList($pc) {
-               if($this->head == null) {
-                       $this->head = $pc;
-                       $this->tail = $this->head;
+       private function addConverter($word, $modifiers, $option) {
+               $formattingInfo = $this->parseModifiers($modifiers);
+               $option = trim($option, "{} ");
+               
+               if (isset($this->converterMap[$word])) {
+                       $converter = $this->getConverter($word, $formattingInfo, $option);
+                       $this->addToChain($converter);  
                } else {
-                       $this->tail->next = $pc;
-                       $this->tail = $this->tail->next;
+                       trigger_error("log4php: Invalid keyword '%$word' in converison pattern. Ignoring keyword.", E_USER_WARNING);
                }
        }
-
+       
        /**
-        * @return string
+        * Determines which converter to use based on the conversion word. Creates 
+        * an instance of the converter using the provided formatting info and 
+        * option and returns it.
+        * 
+        * @param string $word The conversion word.
+        * @param LoggerFormattingInfo $info Formatting info.
+        * @param string $option Converter option.
+        * 
+        * @throws LoggerException 
+        * 
+        * @return LoggerPatternConverter
         */
-       public function extractOption() {
-               if(($this->i < $this->patternLength) and ($this->pattern{$this->i} == '{')) {
-                       $end = strpos($this->pattern, '}' , $this->i);
-                       if($end !== false) {
-                               $r = substr($this->pattern, ($this->i + 1), ($end - $this->i - 1));
-                               $this->i= $end + 1;
-                               return $r;
-                       }
+       private function getConverter($word, $info, $option) {
+               if (!isset($this->converterMap[$word])) {
+                       throw new LoggerException("Invalid keyword '%$word' in converison pattern. Ignoring keyword.");
                }
-               return null;
+               
+               $converterClass = $this->converterMap[$word];
+               if(!class_exists($converterClass)) {
+                       throw new LoggerException("Class '$converterClass' does not exist.");
+               }
+               
+               $converter = new $converterClass($info, $option);
+               if(!($converter instanceof LoggerPatternConverter)) {
+                       throw new LoggerException("Class '$converterClass' is not an instance of LoggerPatternConverter.");
+               }
+               
+               return $converter;
        }
-
-       /**
-        * The option is expected to be in decimal and positive. In case of
-        * error, zero is returned.      
-        */
-       public function extractPrecisionOption() {
-               $opt = $this->extractOption();
-               $r = 0;
-               if($opt !== null) {
-                       if(is_numeric($opt)) {
-                               $r = (int)$opt;
-                               if($r <= 0) {
-                                       $r = 0;
-                               }
-                       }
+       
+       /** Adds a converter to the chain and updates $head and $tail pointers. */
+       private function addToChain(LoggerPatternConverter $converter) {
+               if (!isset($this->head)) {
+                       $this->head = $converter;
+                       $this->tail = $this->head;
+               } else {
+                       $this->tail->next = $converter;
+                       $this->tail = $this->tail->next;
                }
-               return $r;
        }
-
        
-       /** Parser.
+       /**
+        * Parses the formatting modifiers and produces the corresponding 
+        * LoggerFormattingInfo object.
         * 
-        * @return LoggerPatternConverter Returns $this->head.
+        * @param string $modifier
+        * @return LoggerFormattingInfo
+        * @throws LoggerException
         */
-       public function parse() {
-               $c = '';
-               $this->i = 0;
-               $this->currentLiteral = '';
-               while($this->i < $this->patternLength) {
-                       $c = $this->pattern{$this->i++};
-
-                       switch($this->state) {
-                               case self::LITERAL_STATE:
-                                       // In literal state, the last char is always a literal.
-                                       if($this->i == $this->patternLength) {
-                                               $this->currentLiteral .= $c;
-                                               continue;
-                                       }
-                                       if($c == self::ESCAPE_CHAR) {
-                                               // peek at the next char.
-                                               switch($this->pattern{$this->i}) {
-                                                       case self::ESCAPE_CHAR:
-                                                               $this->currentLiteral .= $c;
-                                                               $this->i++; // move pointer
-                                                               break;
-                                                       case 'n':
-                                                               $this->currentLiteral .= PHP_EOL;
-                                                               $this->i++; // move pointer
-                                                               break;
-                                                       default:
-                                                               if(strlen($this->currentLiteral) != 0) {
-                                                                       $this->addToList(new LoggerLiteralPatternConverter($this->currentLiteral));
-                                                               }
-                                                               $this->currentLiteral = $c;
-                                                               $this->state = self::CONVERTER_STATE;
-                                                               $this->formattingInfo->reset();
-                                               }
-                                       } else {
-                                               $this->currentLiteral .= $c;
-                                       }
-                                       break;
-                               case self::CONVERTER_STATE:
-                                               $this->currentLiteral .= $c;
-                                               switch($c) {
-                                               case '-':
-                                                       $this->formattingInfo->leftAlign = true;
-                                                       break;
-                                               case '.':
-                                                       $this->state = self::DOT_STATE;
-                                                               break;
-                                               default:
-                                                       if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
-                                                               $this->formattingInfo->min = ord($c) - ord('0');
-                                                               $this->state = self::MIN_STATE;
-                                                       } else {
-                                                               $this->finalizeConverter($c);
-                                                       }
-                                               } // switch
-                                       break;
-                               case self::MIN_STATE:
-                                       $this->currentLiteral .= $c;
-                                       if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
-                                               $this->formattingInfo->min = ($this->formattingInfo->min * 10) + (ord($c) - ord('0'));
-                                       } else if ($c == '.') {
-                                               $this->state = self::DOT_STATE;
-                                       } else {
-                                               $this->finalizeConverter($c);
-                                       }
-                                       break;
-                               case self::DOT_STATE:
-                                       $this->currentLiteral .= $c;
-                                       if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
-                                               $this->formattingInfo->max = ord($c) - ord('0');
-                                               $this->state = self::MAX_STATE;
-                                       } else {
-                                               $this->state = self::LITERAL_STATE;
-                                       }
-                                       break;
-                               case self::MAX_STATE:
-                                       $this->currentLiteral .= $c;
-                                       if(ord($c) >= ord('0') and ord($c) <= ord('9')) {
-                                               $this->formattingInfo->max = ($this->formattingInfo->max * 10) + (ord($c) - ord('0'));
-                                       } else {
-                                               $this->finalizeConverter($c);
-                                               $this->state = self::LITERAL_STATE;
-                                       }
-                                       break;
-                       } // switch
-               } // while
-               if(strlen($this->currentLiteral) != 0) {
-                       $this->addToList(new LoggerLiteralPatternConverter($this->currentLiteral));
+       private function parseModifiers($modifiers) {
+               $info = new LoggerFormattingInfo();
+       
+               // If no modifiers are given, return default values
+               if (empty($modifiers)) {
+                       return $info;
                }
-               return $this->head;
-       }
-
-       public function finalizeConverter($c) {
-               $pc = null;
-               switch($c) {
-                       case 'c':
-                               $pc = new LoggerCategoryPatternConverter($this->formattingInfo, $this->extractPrecisionOption());
-                               $this->currentLiteral = '';
-                               break;
-                       case 'C':
-                               $pc = new LoggerClassNamePatternConverter($this->formattingInfo, self::CLASS_LOCATION_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'd':
-                               $dateFormatStr = self::DATE_FORMAT_ISO8601; // ISO8601_DATE_FORMAT;
-                               $dOpt = $this->extractOption();
-
-                               if($dOpt !== null)
-                                       $dateFormatStr = $dOpt;
-                                       
-                               if($dateFormatStr == 'ISO8601') {
-                                       $df = self::DATE_FORMAT_ISO8601;
-                               } else if($dateFormatStr == 'ABSOLUTE') {
-                                       $df = self::DATE_FORMAT_ABSOLUTE;
-                               } else if($dateFormatStr == 'DATE') {
-                                       $df = self::DATE_FORMAT_DATE;
-                               } else {
-                                       $df = $dateFormatStr;
-                                       if($df == null) {
-                                               $df = self::DATE_FORMAT_ISO8601;
-                                       }
-                               }
-                               $pc = new LoggerDatePatternConverter($this->formattingInfo, $df);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'F':
-                               $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::FILE_LOCATION_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'l':
-                               $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::FULL_LOCATION_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'L':
-                               $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::LINE_LOCATION_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'm':
-                               $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::MESSAGE_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'M':
-                               $pc = new LoggerLocationPatternConverter($this->formattingInfo, self::METHOD_LOCATION_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'p':
-                               $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::LEVEL_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'r':
-                               $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::RELATIVE_TIME_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 't':
-                               $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::THREAD_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'x':
-                               $pc = new LoggerBasicPatternConverter($this->formattingInfo, self::NDC_CONVERTER);
-                               $this->currentLiteral = '';
-                               break;
-                       case 'X':
-                               $xOpt = $this->extractOption();
-                               $pc = new LoggerMDCPatternConverter($this->formattingInfo, $xOpt);
-                               $this->currentLiteral = '';
-                               break;
-                       default:
-                               $pc = new LoggerLiteralPatternConverter($this->currentLiteral);
-                               $this->currentLiteral = '';
+       
+               // Validate
+               $pattern = '/^(-?[0-9]+)?\.?-?[0-9]+$/';
+               if (!preg_match($pattern, $modifiers)) {
+                       trigger_error("log4php: Invalid modifier in conversion pattern: [$modifiers]. Ignoring modifier.", E_USER_WARNING);
+                       return $info;
                }
-               $this->addConverter($pc);
-       }
-
-       public function addConverter($pc) {
-               $this->currentLiteral = '';
-               // Add the pattern converter to the list.
-               $this->addToList($pc);
-               // Next pattern is assumed to be a literal.
-               $this->state = self::LITERAL_STATE;
-               // Reset formatting info
-               $this->formattingInfo->reset();
+       
+               $parts = explode('.', $modifiers);
+       
+               if (!empty($parts[0])) {
+                       $minPart = (integer) $parts[0];
+                       $info->min = abs($minPart);
+                       $info->padLeft = ($minPart > 0);
+               }
+       
+               if (!empty($parts[1])) {
+                       $maxPart = (integer) $parts[1];
+                       $info->max = abs($maxPart);
+                       $info->trimLeft = ($maxPart > 0);
+               }
+       
+               return $info;
        }
 }
 
diff --git a/src/main/php/helpers/LoggerUtils.php b/src/main/php/helpers/LoggerUtils.php
new file mode 100644 (file)
index 0000000..bc7366e
--- /dev/null
@@ -0,0 +1,123 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Contains various helper methods.\r
+ * \r
+ * @package log4php\r
+ * @subpackage helpers\r
+ * @since 2.3\r
+ */\r
+class LoggerUtils {\r
+       \r
+       /**\r
+        * Splits a fully qualified class name into fragments delimited by the \r
+        * namespace separator (\). \r
+        * \r
+        * For backward compatibility, a dot (.) can be used as a delimiter as\r
+        * well. \r
+        * \r
+        * @param string $name\r
+        * \r
+        * @return array Class name split into fragments.\r
+        */\r
+       public static function tokenizeClassName($name) {\r
+               $name = str_replace('.', '\\', $name);\r
+               $name = trim($name, ' \\');\r
+               $fragments = explode('\\', $name);\r
+               \r
+               foreach($fragments as $key => $fragment) {\r
+                       if (trim($fragment) === '') {\r
+                               unset($fragments[$key]);\r
+                       }\r
+               }\r
+               \r
+               return $fragments;\r
+       }\r
+       \r
+       /**\r
+        * Attempts to shorten the given class name to the desired length.\r
+        * \r
+        * This is done by separating the class name into fragments (delimited\r
+        * by \ or .) and trimming individual fragments, starting with the left,\r
+        * until desired length has been reached. \r
+        * \r
+        * The final fragment (i.e. class name) will never be shortened so the \r
+        * result may still be longer than given length.\r
+        * \r
+        * @param string $name The (qualified) class name.  \r
+        * @param integer $length The length to shorten to. If null or 0 is given,\r
+        * the name will be returned without shortening. \r
+        */\r
+       public static function shortenClassName($name, $length) {\r
+               if ($length === null || $length < 0) {\r
+                       return $name;\r
+               }\r
+               \r
+               $name = str_replace('.', '\\', $name);\r
+               $name = trim($name, ' \\');\r
+               \r
+               // Check if any shortening is required\r
+               $currentLength = strlen($name);\r
+               if ($currentLength <= $length) {\r
+                       return $name;\r
+               }\r
+       \r
+               // Split name into fragments\r
+               $fragments = explode('\\', $name);\r
+\r
+               // If zero length is specified, return only last fragment\r
+               if ($length == 0) {\r
+                       return array_pop($fragments);\r
+               }\r
+               \r
+               // If the name splits to only one fragment, then it cannot be shortened\r
+               $count = count($fragments);\r
+               if ($count == 1) {\r
+                       return $name;\r
+               }\r
+       \r
+               foreach($fragments as $key => &$fragment) {\r
+       \r
+                       // Never shorten last fragment\r
+                       if ($key == $count - 1) {\r
+                               break;\r
+                       }\r
+       \r
+                       // Check for empty fragments (shouldn't happen but it's possible)\r
+                       $fragLen = strlen($fragment);\r
+                       if ($fragLen <= 1) {\r
+                               continue;\r
+                       }\r
+       \r
+                       // Shorten fragment to one character and check if total length satisfactory\r
+                       $fragment = substr($fragment, 0, 1);\r
+                       $currentLength = $currentLength - $fragLen + 1;\r
+       \r
+                       if ($currentLength <= $length) {\r
+                               break;\r
+                       }\r
+               }\r
+               unset($fragment);\r
+       \r
+               return implode('\\', $fragments);\r
+       }\r
+}\r
+\r
index 057d700..75db865 100644 (file)
 /**
  * A flexible layout configurable with pattern string.
  *
- * <p>Example:</p>
- * 
- * {@example ../../examples/php/layout_pattern.php 19}<br>
- * 
- * <p>with the following properties file:</p>
- * 
- * {@example ../../examples/resources/layout_pattern.properties 18}<br>
- * 
- * <p>would print the following:</p>
- * 
- * <pre>
- * 2009-09-09 00:27:35,787 [INFO] root: Hello World! (at src/examples/php/layout_pattern.php line 6)
- * 2009-09-09 00:27:35,787 [DEBUG] root: Second line (at src/examples/php/layout_pattern.php line 7)
- * </pre>
- *
- * <p>The conversion pattern is closely related to the conversion pattern of the printf function in C.
- * A conversion pattern is composed of literal text and format control expressions called conversion specifiers.
- * You are free to insert any literal text within the conversion pattern.</p> 
- *
- * <p>Each conversion specifier starts with a percent sign (%) and is followed by optional 
- * format modifiers and a conversion character.</p>
- * 
- * <p>The conversion character specifies the type of data, e.g. category, priority, date, thread name. 
- * The format modifiers control such things as field width, padding, left and right justification.</p>
- * 
- * <p>Note that there is no explicit separator between text and conversion specifiers.</p>
- * 
- * <p>The pattern parser knows when it has reached the end of a conversion specifier when it reads a conversion character. 
- * In the example above the conversion specifier %-5p means the priority of the logging event should be 
- * left justified to a width of five characters.</p> 
- *
- * Not all log4j conversion characters are implemented. The recognized conversion characters are:
- * - <b>c</b> Used to output the category of the logging event. The category conversion specifier can be optionally followed by precision specifier, that is a decimal constant in brackets. 
- *         If a precision specifier is given, then only the corresponding number of right most components of the category name will be printed. 
- *         By default the category name is printed in full. 
- *         For example, for the category name "a.b.c" the pattern %c{2} will output "b.c". 
- * - <b>C</b> Used to output the fully qualified class name of the caller issuing the logging request. 
- *         This conversion specifier can be optionally followed by precision specifier, that is a decimal constant in brackets. 
- *         If a precision specifier is given, then only the corresponding number of right most components of the class name will be printed. 
- *         By default the class name is output in fully qualified form. 
- *         For example, for the class name "org.apache.xyz.SomeClass", the pattern %C{1} will output "SomeClass". 
- * - <b>d</b> Used to output the date of the logging event. 
- *         The date conversion specifier may be followed by a date format specifier enclosed between braces.
- *         The format specifier follows the {@link PHP_MANUAL#date} function.
- *         Note that the special character <b>u</b> is used to as microseconds replacement (to avoid replacement,
- *         use <b>\u</b>).  
- *         For example, %d{H:i:s,u} or %d{d M Y H:i:s,u}. If no date format specifier is given then ISO8601 format is assumed. 
- *         The date format specifier admits the same syntax as the time pattern string of the SimpleDateFormat. 
- *         It is recommended to use the predefined log4php date formatters. 
- *         These can be specified using one of the strings "ABSOLUTE", "DATE" and "ISO8601" for specifying 
- *         AbsoluteTimeDateFormat, DateTimeDateFormat and respectively ISO8601DateFormat. 
- *         For example, %d{ISO8601} or %d{ABSOLUTE}. 
- * - <b>F</b> Used to output the file name where the logging request was issued. 
- * - <b>l</b> Used to output location information of the caller which generated the logging event. 
- * - <b>L</b> Used to output the line number from where the logging request was issued.
- * - <b>m</b> Used to output the application supplied message associated with the logging event.
- * - <b>M</b> Used to output the method name where the logging request was issued.  
- * - <b>p</b> Used to output the priority of the logging event.
- * - <b>r</b> Used to output the number of milliseconds elapsed since the start of 
- *            the application until the creation of the logging event. 
- * - <b>t</b> Used to output the name of the thread that generated the logging event.
- * - <b>x</b> Used to output the NDC (nested diagnostic context) associated with 
- *            the thread that generated the logging event.  
- * - <b>X</b> Used to output the MDC (mapped diagnostic context) associated with 
- *            the thread that generated the logging event. 
- *            The X conversion character must be followed by the key for the map placed between braces, 
- *            as in <i>%X{clientNumber}</i> where clientNumber is the key.
- *            The value in the MDC corresponding to the key will be output.
- *            See {@link LoggerMDC} class for more details. 
- * - <b>%</b> The sequence %% outputs a single percent sign.  
- *
- * <p>By default the relevant information is output as is. 
- *  However, with the aid of format modifiers it is possible to change the minimum field width, 
- *  the maximum field width and justification.</p> 
- *
- * <p>The optional format modifier is placed between the percent sign and the conversion character.</p>
- * <p>The first optional format modifier is the left justification flag which is just the minus (-) character. 
- *  Then comes the optional minimum field width modifier. 
- *  This is a decimal constant that represents the minimum number of characters to output. 
- *  If the data item requires fewer characters, it is padded on either the left or the right until the minimum width is reached. The default is to pad on the left (right justify) but you can specify right padding with the left justification flag. The padding character is space. If the data item is larger than the minimum field width, the field is expanded to accommodate the data. 
- *  The value is never truncated.</p>
- * 
- * <p>This behavior can be changed using the maximum field width modifier which is designated by a period 
- *  followed by a decimal constant. 
- *  If the data item is longer than the maximum field, 
- *  then the extra characters are removed from the beginning of the data item and not from the end. 
- *  For example, it the maximum field width is eight and the data item is ten characters long, 
- *  then the first two characters of the data item are dropped. 
- *  This behavior deviates from the printf function in C where truncation is done from the end.</p> 
- *
- * <p>Below are various format modifier examples for the category conversion specifier.</p> 
- * <pre>
- *   Format modifier  left justify  minimum width  maximum width  comment
- *   %20c             false         20             none           Left pad with spaces if the category name 
- *                                                                is less than 20 characters long.
- *   %-20c            true          20             none           Right pad with spaces if the category name 
- *                                                                is less than 20 characters long.  
- *   %.30c            NA            none           30             Truncate from the beginning if the category name 
- *                                                                is longer than 30 characters.  
- *   %20.30c          false         20             30             Left pad with spaces if the category name 
- *                                                                is shorter than 20 characters. 
- *                                                                However, if category name is longer than 30 chars, 
- *                                                                then truncate from the beginning.  
- *   %-20.30c         true          20             30             Right pad with spaces if the category name is 
- *                                                                shorter than 20 chars. 
- *                                                                However, if category name is longer than 30 chars, 
- *                                                                then truncate from the beginning.  
- * </pre>
- * 
- * @version $Revision$
- * @package log4php
- * @subpackage layouts
- * @since 0.3 
  */
 class LoggerLayoutPattern extends LoggerLayout {
-       /** Default conversion Pattern */
+       
+       /** Default conversion pattern */
        const DEFAULT_CONVERSION_PATTERN = '%m%n';
 
        /** Default conversion TTCC Pattern */
@@ -144,13 +32,89 @@ class LoggerLayoutPattern extends LoggerLayout {
 
        /** The conversion pattern. */ 
        protected $pattern = self::DEFAULT_CONVERSION_PATTERN;
+       
+       /** Maps conversion keywords to the relevant converter. */
+       protected static $defaultConverterMap = array(
+               'c' => 'LoggerPatternConverterLogger',
+               'lo' => 'LoggerPatternConverterLogger',
+               'logger' => 'LoggerPatternConverterLogger',
+               
+               'C' => 'LoggerPatternConverterClass',
+               'class' => 'LoggerPatternConverterClass',
+               
+               'cookie' => 'LoggerPatternConverterCookie',
+               
+               'd' => 'LoggerPatternConverterDate',
+               'date' => 'LoggerPatternConverterDate',
+               
+               'e' => 'LoggerPatternConverterEnvironment',
+               'env' => 'LoggerPatternConverterEnvironment',
+               
+               'ex' => 'LoggerPatternConverterThrowable',
+               'throwable' => 'LoggerPatternConverterThrowable',
+               
+               'F' => 'LoggerPatternConverterFile',
+               'file' => 'LoggerPatternConverterFile',
+               
+               'L' => 'LoggerPatternConverterLine',
+               'line' => 'LoggerPatternConverterLine',
+               
+               'm' => 'LoggerPatternConverterMessage',
+               'msg' => 'LoggerPatternConverterMessage',
+               'message' => 'LoggerPatternConverterMessage',
+               
+               'M' => 'LoggerPatternConverterMethod',
+               'method' => 'LoggerPatternConverterMethod',
+               
+               'n' => 'LoggerPatternConverterNewLine',
+               'newline' => 'LoggerPatternConverterNewLine',
+               
+               'p' => 'LoggerPatternConverterLevel',
+               'le' => 'LoggerPatternConverterLevel',
+               'level' => 'LoggerPatternConverterLevel',
+       
+               'r' => 'LoggerPatternConverterRelative',
+               'relative' => 'LoggerPatternConverterRelative',
+               
+               'req' => 'LoggerPatternConverterRequest',
+               'request' => 'LoggerPatternConverterRequest',
+               
+               's' => 'LoggerPatternConverterServer',
+               'server' => 'LoggerPatternConverterServer',
+               
+               'ses' => 'LoggerPatternConverterSession',
+               'session' => 'LoggerPatternConverterSession',
+               
+               'sid' => 'LoggerPatternConverterSessionID',
+               'sessionid' => 'LoggerPatternConverterSessionID',
+       
+               't' => 'LoggerPatternConverterProcess',
+               'pid' => 'LoggerPatternConverterProcess',
+               'process' => 'LoggerPatternConverterProcess',
+               
+               'x' => 'LoggerPatternConverterNDC',
+               'ndc' => 'LoggerPatternConverterNDC',
+                       
+               'X' => 'LoggerPatternConverterMDC',
+               'mdc' => 'LoggerPatternConverterMDC',
+       );
 
+       protected $converterMap = array();
+       
        /** 
         * Head of a chain of Converters.
         * @var LoggerPatternConverter 
         */
        private $head;
 
+       public static function getDefaultConverterMap() {
+               return self::$defaultConverterMap;
+       }
+       
+       public function __construct() {
+               $this->converterMap = self::$defaultConverterMap;
+       }
+       
        /**
         * Set the <b>ConversionPattern</b> option. This is the string which
         * controls formatting and consists of a mix of literal content and
@@ -158,10 +122,17 @@ class LoggerLayoutPattern extends LoggerLayout {
         */
        public function setConversionPattern($conversionPattern) {
                $this->pattern = $conversionPattern;
-               $patternParser = new LoggerPatternParser($this->pattern);
-               $this->head = $patternParser->parse();
        }
-
+       
+       public function activateOptions() {
+               if (!isset($this->pattern)) {
+                       throw new LoggerException("Mandatory parameter 'conversionPattern' is not set.");
+               }
+               
+               $parser = new LoggerPatternParser($this->pattern, $this->converterMap);
+               $this->head = $parser->parse();
+       }
+       
        /**
         * Produces a formatted string as specified by the conversion pattern.
         *
@@ -170,36 +141,11 @@ class LoggerLayoutPattern extends LoggerLayout {
         */
        public function format(LoggerLoggingEvent $event) {
                $sbuf = '';
-               $c = $this->head;
-               while ($c !== null) {
-                       $c->format($sbuf, $event);
-                       $c = $c->next;
+               $converter = $this->head;
+               while ($converter !== null) {
+                       $converter->format($sbuf, $event);
+                       $converter = $converter->next;
                }
                return $sbuf;
        }
-       
-       /**
-        * Returns an array with the formatted elements.
-        * 
-        * This method is mainly used for the prepared statements of {@see LoggerAppenderPDO}.
-        * 
-        * It requires {@link $this->pattern} to be a comma separated string of patterns like
-        * e.g. <code>%d,%c,%p,%m,%t,%F,%L</code>.
-        * 
-        * @return array(string)   An array of the converted elements i.e. timestamp, message, filename etc.
-        */
-       public function formatToArray(LoggerLoggingEvent $event) {
-               $results = array();
-               $c = $this->head;
-               while ($c !== null) {
-                       if ( ! $c instanceOf LoggerLiteralPatternConverter) {
-                               $sbuf = null;
-                               $c->format($sbuf, $event);
-                               $results[] = $sbuf;
-                       }
-                       $c = $c->next;
-               }
-               return $results;
-       }
-       
 }
\ No newline at end of file
diff --git a/src/main/php/pattern/LoggerPatternConverter.php b/src/main/php/pattern/LoggerPatternConverter.php
new file mode 100644 (file)
index 0000000..2552aaa
--- /dev/null
@@ -0,0 +1,131 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * LoggerPatternConverter is an abstract class that provides the formatting \r
+ * functionality that derived classes need.\r
+ * \r
+ * <p>Conversion specifiers in a conversion patterns are parsed to\r
+ * individual PatternConverters. Each of which is responsible for\r
+ * converting a logging event in a converter specific manner.</p>\r
+ * \r
+ * @version $Revision: 1166187 $\r
+ * @package log4php\r
+ * @subpackage helpers\r
+ * @since 0.3\r
+ */\r
+abstract class LoggerPatternConverter {\r
+       \r
+       /**\r
+        * Next converter in the converter chain.\r
+        * @var LoggerPatternConverter \r
+        */\r
+       public $next = null;\r
+       \r
+       /**\r
+        * Formatting information, parsed from pattern modifiers. \r
+        * @var LoggerFormattingInfo\r
+        */\r
+       protected $formattingInfo;\r
+       \r
+       /**\r
+        * Converter-specific formatting options.\r
+        * @var array\r
+        */\r
+       protected $option;\r
+\r
+       /**\r
+        * Constructor \r
+        * @param LoggerFormattingInfo $formattingInfo\r
+        * @param array $option\r
+        */\r
+       public function __construct(LoggerFormattingInfo $formattingInfo = null, $option = null) {  \r
+               $this->formattingInfo = $formattingInfo;\r
+               $this->option = $option;\r
+               $this->activateOptions();\r
+       }\r
+       \r
+       /**\r
+        * Called in constructor. Converters which need to process the options \r
+        * can override this method. \r
+        */\r
+       public function activateOptions() { }\r
+  \r
+       /**\r
+        * Converts the logging event to the desired format. Derived pattern \r
+        * converters must implement this method.\r
+        *\r
+        * @param LoggerLoggingEvent $event\r
+        */\r
+       abstract public function convert(LoggerLoggingEvent $event);\r
+\r
+       /**\r
+        * Converts the event and formats it according to setting in the \r
+        * Formatting information object.\r
+        *\r
+        * @param string &$sbuf string buffer to write to\r
+        * @param LoggerLoggingEvent $event Event to be formatted.\r
+        */\r
+       public function format(&$sbuf, $event) {\r
+               $string = $this->convert($event);\r
+               \r
+               if (!isset($this->formattingInfo)) {\r
+                       $sbuf .= $string;\r
+                       return; \r
+               }\r
+               \r
+               $fi = $this->formattingInfo;\r
+               \r
+               // Empty string\r
+               if($string === '' || is_null($string)) {\r
+                       if($fi->min > 0) {\r
+                               $sbuf .= str_repeat(' ', $fi->min);\r
+                       }\r
+                       return;\r
+               }\r
+               \r
+               $len = strlen($string);\r
+       \r
+               // Trim the string if needed\r
+               if($len > $fi->max) {\r
+                       if ($fi->trimLeft) {\r
+                               $sbuf .= substr($string, $len - $fi->max, $fi->max);\r
+                       } else {\r
+                               $sbuf .= substr($string , 0, $fi->max);\r
+                       }\r
+               }\r
+               \r
+               // Add padding if needed\r
+               else if($len < $fi->min) {\r
+                       if($fi->padLeft) {\r
+                               $sbuf .= str_repeat(' ', $fi->min - $len);\r
+                               $sbuf .= $string;\r
+                       } else {\r
+                               $sbuf .= $string;\r
+                               $sbuf .= str_repeat(' ', $fi->min - $len);\r
+                       }\r
+               }\r
+               \r
+               // No action needed\r
+               else {\r
+                       $sbuf .= $string;\r
+               }\r
+       }\r
+}\r
@@ -1,50 +1,34 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerMDCPatternConverter extends LoggerPatternConverter {
-
-       /**
-        * @var string
-        */
-       private $key;
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param string $key
-        */
-       public function __construct($formattingInfo, $key) {
-               parent::__construct($formattingInfo);
-               $this->key = $key;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function convert($event) {
-               return $event->getMDC($this->key);
-       }
-}
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the fully qualified class name of the class from which the logging \r
+ * request was issued.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterClass extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getLocationInformation()->getClassName();\r
+       }\r
+}\r
\ No newline at end of file
diff --git a/src/main/php/pattern/LoggerPatternConverterCookie.php b/src/main/php/pattern/LoggerPatternConverterCookie.php
new file mode 100644 (file)
index 0000000..53c3571
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from the $_COOKIE superglobal array corresponding to the \r
+ * given key. If no key is given, return all values.\r
+ * \r
+ * Options:\r
+ *  [0] $_COOKIE key value\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterCookie extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_COOKIE';\r
+}
\ No newline at end of file
diff --git a/src/main/php/pattern/LoggerPatternConverterDate.php b/src/main/php/pattern/LoggerPatternConverterDate.php
new file mode 100644 (file)
index 0000000..1be3e63
--- /dev/null
@@ -0,0 +1,89 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the date/time of the logging request.\r
+ * \r
+ * Option: the datetime format, as used by the date() function. If \r
+ * the option is not given, the default format 'c' will be used.\r
+ * \r
+ * There are several "special" values which can be given for this option:\r
+ * 'ISO8601', 'ABSOLUTE' and 'DATE'.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterDate extends LoggerPatternConverter {\r
+\r
+       const DATE_FORMAT_ISO8601 = 'c';\r
+       \r
+       const DATE_FORMAT_ABSOLUTE = 'H:i:s';\r
+       \r
+       const DATE_FORMAT_DATE = 'd M Y H:i:s.u';\r
+       \r
+       private $format = self::DATE_FORMAT_ISO8601;\r
+       \r
+       private $specials = array(\r
+               'ISO8601' => self::DATE_FORMAT_ISO8601,\r
+               'ABSOLUTE' => self::DATE_FORMAT_ABSOLUTE,\r
+               'DATE' => self::DATE_FORMAT_DATE,\r
+       );\r
+       \r
+       private $useLocalDate = false;\r
+       \r
+       public function activateOptions() {\r
+               \r
+               // Parse the option (date format)\r
+               if (isset($this->option)) {\r
+                       if(isset($this->specials[$this->option])) {\r
+                               $this->format = $this->specials[$this->option];\r
+                       } else {\r
+                               $this->format = $this->option;\r
+                       }\r
+               }\r
+               \r
+               // Check whether the pattern contains milliseconds (u)\r
+               if (preg_match('/(?<!\\\\)u/', $this->format)) {\r
+                       $this->useLocalDate = true;\r
+               }\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               if ($this->useLocalDate) {\r
+                       return $this->date($this->format, $event->getTimeStamp());\r
+               }\r
+               return date($this->format, $event->getTimeStamp());\r
+       }\r
+       \r
+       /**\r
+        * Currently, PHP date() function always returns zeros for milliseconds (u)\r
+        * on Windows. This is a replacement function for date() which correctly \r
+        * displays milliseconds on all platforms. \r
+        * \r
+        * It is slower than PHP date() so it should only be used if necessary. \r
+        */\r
+       private function date($format, $utimestamp) {\r
+               $timestamp = floor($utimestamp);\r
+               $ms = floor(($utimestamp - $timestamp) * 1000);\r
+               $ms = str_pad($ms, 3, '0', STR_PAD_LEFT);\r
+       \r
+               return date(preg_replace('`(?<!\\\\)u`', $ms, $format), $timestamp);\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterEnvironment.php b/src/main/php/pattern/LoggerPatternConverterEnvironment.php
new file mode 100644 (file)
index 0000000..185ccbe
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from the $_ENV superglobal array corresponding to the \r
+ * given key.\r
+ * \r
+ * Options:\r
+ *  [0] $_ENV key value\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterEnvironment extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_ENV';\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterFile.php b/src/main/php/pattern/LoggerPatternConverterFile.php
new file mode 100644 (file)
index 0000000..0b3176e
--- /dev/null
@@ -0,0 +1,32 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the name of the file from which the logging request was issued. \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterFile extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getLocationInformation()->getFileName();\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterLevel.php b/src/main/php/pattern/LoggerPatternConverterLevel.php
new file mode 100644 (file)
index 0000000..ba9c80f
--- /dev/null
@@ -0,0 +1,32 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the event's level.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterLevel extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getLevel()->toString();\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterLine.php b/src/main/php/pattern/LoggerPatternConverterLine.php
new file mode 100644 (file)
index 0000000..31c3cf8
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the line number within the file from which the logging request was \r
+ * issued. \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterLine extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getLocationInformation()->getLineNumber();\r
+       }\r
+}\r
@@ -1,44 +1,38 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerCategoryPatternConverter extends LoggerNamedPatternConverter {
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param integer $precision
-        */
-       public function __construct($formattingInfo, $precision) {
-               parent::__construct($formattingInfo, $precision);
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function getFullyQualifiedName($event) {
-               return $event->getLoggerName();
-       }
-}
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the literal value passed in the constructor, without modifications.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterLiteral extends LoggerPatternConverter {\r
+\r
+       private $literalValue;\r
+       \r
+       public function __construct($literalValue) {\r
+               $this->literalValue = $literalValue;\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $this->literalValue;\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterLocation.php b/src/main/php/pattern/LoggerPatternConverterLocation.php
new file mode 100644 (file)
index 0000000..16bed8d
--- /dev/null
@@ -0,0 +1,37 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the line number within the file from which the logging request was \r
+ * issued. \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterLine extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return \r
+                       $event->getLocationInformation()->getClassName() . '.' .\r
+                       $event->getLocationInformation()->getMethodName() . '(' .\r
+                       $event->getLocationInformation()->getFileName() . ':' .\r
+                       $event->getLocationInformation()->getLineNumber() . ')';\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterLogger.php b/src/main/php/pattern/LoggerPatternConverterLogger.php
new file mode 100644 (file)
index 0000000..72b4a49
--- /dev/null
@@ -0,0 +1,114 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the name of the logger which created the logging request.\r
+ * \r
+ * Takes one option, which is an integer. If the option is given, the logger \r
+ * name will be shortened to the given length, if possible.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterLogger extends LoggerPatternConverter {\r
+\r
+       /** Length to which to shorten the name. */\r
+       private $length;\r
+       \r
+       /** Holds processed logger names. */\r
+       private $cache = array();\r
+       \r
+       public function activateOptions() {\r
+               // Parse the option (desired output length)\r
+               if (isset($this->option) && is_numeric($this->option) && $this->option >= 0) {\r
+                       $this->length = (integer) $this->option;\r
+               }\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               $name = $event->getLoggerName();\r
+               \r
+               if (!isset($this->cache[$name])) {\r
+\r
+                       // If length is set return shortened logger name \r
+                       if (isset($this->length)) {\r
+                               $this->cache[$name] = $this->shorten($name, $this->length);\r
+                       } \r
+                        \r
+                       // If no length is specified return full logger name\r
+                       else {\r
+                               $this->cache[$name] = $name;\r
+                       }\r
+               } \r
+               \r
+               return $this->cache[$name];\r
+       }\r
+       \r
+       /** \r
+        * Attempts to shorten the given name to the desired length by trimming \r
+        * name fragments. See docs for examples.\r
+        */\r
+       private function shorten($name, $length) {\r
+       \r
+               $currentLength = strlen($name);\r
+       \r
+               // Check if any shortening is required\r
+               if ($currentLength <= $length) {\r
+                       return $name;\r
+               }\r
+       \r
+               // Split name into fragments\r
+               $name = str_replace('.', '\\', $name);\r
+               $name = trim($name, ' \\');\r
+               $fragments = explode('\\', $name);\r
+               $count = count($fragments);\r
+       \r
+               // If the name splits to only one fragment, then it cannot be shortened\r
+               if ($count == 1) {\r
+                       return $name;\r
+               }\r
+               \r
+               foreach($fragments as $key => &$fragment) {\r
+       \r
+                       // Never shorten last fragment\r
+                       if ($key == $count - 1) {\r
+                               break;\r
+                       }\r
+       \r
+                       // Check for empty fragments (shouldn't happen but it's possible)\r
+                       $fragLen = strlen($fragment);\r
+                       if ($fragLen <= 1) {\r
+                               continue;\r
+                       }\r
+       \r
+                       // Shorten fragment to one character and check if total length satisfactory\r
+                       $fragment = substr($fragment, 0, 1);\r
+                       $currentLength = $currentLength - $fragLen + 1;\r
+       \r
+                       if ($currentLength <= $length) {\r
+                               break;\r
+                       }\r
+               }\r
+               unset($fragment);\r
+       \r
+               return implode('\\', $fragments);\r
+       }\r
+       \r
+}\r
@@ -1,53 +1,53 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerDatePatternConverter extends LoggerPatternConverter {
-
-       /**
-        * @var string
-        */
-       private $df;
-       
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param string $df
-        */
-       public function __construct($formattingInfo, $df) {
-               parent::__construct($formattingInfo);
-               $this->df = $df;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function convert($event) {
-               $timeStamp = $event->getTimeStamp();
-               $usecs = round(($timeStamp - (int)$timeStamp) * 1000);
-               $df = preg_replace('/((?<!\\\\)(?:\\\\{2})*)u/', '${1}' . sprintf('%03d', $usecs), $this->df);
-               return date($df, $timeStamp);
-       }
-}
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the Mapped Diagnostic Context value corresponding to the given key.\r
+ * \r
+ * Options:\r
+ *  [0] the MDC key\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterMDC extends LoggerPatternConverter {\r
+\r
+       private $key;\r
+\r
+       public function activateOptions() {\r
+               if (isset($this->option) && $this->option !== '') {\r
+                       $this->key = $this->option;\r
+               }\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               if (isset($this->key)) {\r
+                       return $event->getMDC($this->key);\r
+               } else {\r
+                       $buff = array();\r
+                       $map = $event->getMDCMap();\r
+                       foreach($map as $key => $value) {\r
+                               $buff []= "$key=$value";\r
+                       }\r
+                       return implode(', ', $buff);\r
+               }\r
+       }\r
+}\r
\ No newline at end of file
@@ -1,57 +1,32 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerLiteralPatternConverter extends LoggerPatternConverter {
-       
-       /**
-        * @var string
-        */
-       private $literal;
-
-       /**
-        * Constructor
-        *
-        * @param string $value
-        */
-       public function __construct($value) {
-               $this->literal = $value;
-       }
-
-       /**
-        * @param string &$sbuf
-        * @param LoggerLoggingEvent $event
-        */
-       public function format(&$sbuf, $event) {
-               $sbuf .= $this->literal;
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function convert($event) {
-               return $this->literal;
-       }
-}
-
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the logged message.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterMessage extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getRenderedMessage();\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterMethod.php b/src/main/php/pattern/LoggerPatternConverterMethod.php
new file mode 100644 (file)
index 0000000..2e8c9c4
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the name of the function or method from which the logging request \r
+ * was issued. \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterMethod extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getLocationInformation()->getMethodName();\r
+       }\r
+}\r
@@ -1,45 +1,33 @@
-<?php
-/**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * @package log4php
- */
-
-/**
- * @package log4php
- * @subpackage helpers
- */
-class LoggerClassNamePatternConverter extends LoggerNamedPatternConverter {
-
-       /**
-        * Constructor
-        *
-        * @param string $formattingInfo
-        * @param integer $precision
-        */
-       public function __construct($formattingInfo, $precision) {
-               parent::__construct($formattingInfo, $precision);
-       }
-
-       /**
-        * @param LoggerLoggingEvent $event
-        * @return string
-        */
-       public function getFullyQualifiedName($event) {
-               return $event->getFullQualifiedClassname();
-       }
-}
-
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the full Nested Diagnostic Context.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterNDC extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $event->getNDC();\r
+       }\r
+}\r
\ No newline at end of file
diff --git a/src/main/php/pattern/LoggerPatternConverterNewLine.php b/src/main/php/pattern/LoggerPatternConverterNewLine.php
new file mode 100644 (file)
index 0000000..fe952a2
--- /dev/null
@@ -0,0 +1,32 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns platform-specific newline character(s). \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterNewLine extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return PHP_EOL;\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterProcess.php b/src/main/php/pattern/LoggerPatternConverterProcess.php
new file mode 100644 (file)
index 0000000..1a6a8ed
--- /dev/null
@@ -0,0 +1,32 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the PID of the current process.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterProcess extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return getmypid();\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterRelative.php b/src/main/php/pattern/LoggerPatternConverterRelative.php
new file mode 100644 (file)
index 0000000..f3635a8
--- /dev/null
@@ -0,0 +1,34 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the number of milliseconds elapsed since the start of the \r
+ * application until the creation of the logging event.\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterRelative extends LoggerPatternConverter {\r
+\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               $ts = $event->getTimeStamp() - $event->getStartTime();\r
+               return number_format($ts, 4);\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterRequest.php b/src/main/php/pattern/LoggerPatternConverterRequest.php
new file mode 100644 (file)
index 0000000..35b0362
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from the $_REQUEST superglobal array corresponding to the \r
+ * given key.\r
+ * \r
+ * Options:\r
+ *  [0] $_REQUEST key value\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterRequest extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_REQUEST';\r
+}
\ No newline at end of file
diff --git a/src/main/php/pattern/LoggerPatternConverterServer.php b/src/main/php/pattern/LoggerPatternConverterServer.php
new file mode 100644 (file)
index 0000000..668ae6f
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from the $_SERVER superglobal array corresponding to the \r
+ * given key.\r
+ * \r
+ * Options:\r
+ *  [0] $_SERVER key value\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterServer extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_SERVER';\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterSession.php b/src/main/php/pattern/LoggerPatternConverterSession.php
new file mode 100644 (file)
index 0000000..26cff28
--- /dev/null
@@ -0,0 +1,33 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from the $_SESSION superglobal array corresponding to the \r
+ * given key.\r
+ * \r
+ * Options:\r
+ *  [0] $_SESSION key value\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterSession extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_SESSION';\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterSessionID.php b/src/main/php/pattern/LoggerPatternConverterSessionID.php
new file mode 100644 (file)
index 0000000..ed3cf85
--- /dev/null
@@ -0,0 +1,31 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the active session ID, or an empty string if out of session. \r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterSessionID extends LoggerPatternConverter {\r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return session_id();\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterSuperglobal.php b/src/main/php/pattern/LoggerPatternConverterSuperglobal.php
new file mode 100644 (file)
index 0000000..658d63c
--- /dev/null
@@ -0,0 +1,99 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns a value from a superglobal array corresponding to the \r
+ * given key.\r
+ * \r
+ * Option: the key to look up within the superglobal array\r
+ * \r
+ * Also, it is possible that a superglobal variable is not populated by PHP\r
+ * because of the settings in the variables-order ini directive. In this case\r
+ * the converter will return an empty value.\r
+ * \r
+ * @see http://php.net/manual/en/language.variables.superglobals.php\r
+ * @see http://www.php.net/manual/en/ini.core.php#ini.variables-order\r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+abstract class LoggerPatternConverterSuperglobal extends LoggerPatternConverter {\r
+\r
+       /** \r
+        * Name of the superglobal variable, to be defined by subclasses. \r
+        * For example: "_SERVER" or "_ENV". \r
+        */\r
+       protected $name;\r
+       \r
+       protected $value = '';\r
+       \r
+       public function activateOptions() {\r
+               // Read the key from options array\r
+               if (isset($this->option) && $this->option !== '') {\r
+                       $key = $this->option;\r
+               }\r
+       \r
+               /*\r
+                * There is a bug in PHP which doesn't allow superglobals to be \r
+                * accessed when their name is stored in a variable, e.g.:\r
+                * \r
+                * $name = '_SERVER';\r
+                * $array = $$name;\r
+                * \r
+                * This code does not work when run from within a method (only when run\r
+                * in global scope). But the following code does work: \r
+                * \r
+                * $name = '_SERVER';\r
+                * global $$name;\r
+                * $array = $$name;\r
+                * \r
+                * That's why global is used here.\r
+                */\r
+               global ${$this->name};\r
+                       \r
+               // Check the given superglobal exists. It is possible that it is not initialized.\r
+               if (!isset(${$this->name})) {\r
+                       $class = get_class($this);\r
+                       trigger_error("log4php: $class: Cannot find superglobal variable \${$this->name}.", E_USER_WARNING);\r
+                       return;\r
+               }\r
+               \r
+               $source = ${$this->name};\r
+               \r
+               // When the key is set, display the matching value\r
+               if (isset($key)) {\r
+                       if (isset($source[$key])) {\r
+                               $this->value = $source[$key]; \r
+                       }\r
+               }\r
+               \r
+               // When the key is not set, display all values\r
+               else {\r
+                       $values = array();\r
+                       foreach($source as $key => $value) {\r
+                               $values[] = "$key=$value";\r
+                       }\r
+                       $this->value = implode(', ', $values);                  \r
+               }\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               return $this->value;\r
+       }\r
+}\r
diff --git a/src/main/php/pattern/LoggerPatternConverterThrowable.php b/src/main/php/pattern/LoggerPatternConverterThrowable.php
new file mode 100644 (file)
index 0000000..11b821f
--- /dev/null
@@ -0,0 +1,68 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ *        http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @package log4php\r
+ */\r
+\r
+/**\r
+ * Returns the throwable information linked to the logging event, if any.\r
+ * \r
+ * Option: the maximum stack trace lines to return (returns all if not set)\r
+ * \r
+ * @package log4php\r
+ * @subpackage pattern\r
+ */\r
+class LoggerPatternConverterThrowable extends LoggerPatternConverter {\r
+       \r
+       private $depth;\r
+       \r
+       public function activateOptions() {\r
+               if (isset($this->option) && is_numeric($op) && $op >= 0) {\r
+                       $this->depth = (integer) $this->option;\r
+               }\r
+       }\r
+       \r
+       public function convert(LoggerLoggingEvent $event) {\r
+               \r
+               $info = $event->getThrowableInformation();\r
+               if ($info === null) {\r
+                       return '';\r
+               }\r
+               \r
+               $ex = $info->getThrowable();\r
+               \r
+               // Format exception to string\r
+               $strEx = get_class($ex) . ': "' . $ex->getMessage() . '"' . PHP_EOL;\r
+               $strEx .= 'at '. $ex->getFile() . ':' . $ex->getLine();\r
+               \r
+               // Add trace if required\r
+               if ($this->depth === null || $this->depth > 0) {\r
+                       $trace = $ex->getTrace();\r
+                       foreach($trace as $key => $item) {\r
+                               if (isset($this->depth) && $key > $this->depth) {\r
+                                       break;\r
+                               }\r
+                               $strEx .= PHP_EOL . "#$key " . \r
+                                       "{$item['file']}:{$item['line']} " .\r
+                                       "in {$item['class']}{$item['type']}{$item['function']}()"; \r
+                       }\r
+               }\r
+               \r
+               return $strEx;\r
+       }\r
+}\r
\ No newline at end of file
index dc968e2..74b63d5 100644 (file)
@@ -45,7 +45,7 @@
                                                        <td>conversionPattern</td>\r
                                                        <td>string</td>\r
                                                        <td>No</td>\r
-                                                       <td>%m%n</td>\r
+                                                       <td>%message%newline</td>\r
                                                        <td>String which controls the output. See full specification below.</td>\r
                                                </tr>\r
                                        </tbody>\r
                                \r
                                <h4>Conversion patterns</h4>\r
                                \r
-                                       <p>This is the string which controls formatting and consists of a mix of literal content and \r
-                                       conversion specifiers.</p>\r
+                               <p><strong>Conversion pattern</strong> is a string which controls the formatting of logging \r
+                               events. It controls how logging events will be transformed into strings by the layout.</p>\r
+                       \r
+                               <p>The conversion pattern is closely related to the conversion pattern of the \r
+                               <a href="http://www.cplusplus.com/reference/clibrary/cstdio/printf" target="_blank">printf</a> \r
+                               function in C(++). It is composed of literal text and format control expressions called <em>conversion \r
+                               specifiers</em>.</p>\r
                                \r
-                                       <p>The conversion pattern is closely related to the conversion pattern of the \r
-                                       <a href="http://www.cplusplus.com/reference/clibrary/cstdio/printf" target="_blank">printf</a> \r
-                                       function in C. It is composed of literal text and format control expressions called conversion \r
-                                       specifiers. You are free to insert any literal text within the conversion pattern.</p>\r
-                                       \r
-                                       <p>Each conversion specifier starts with a percent sign (%) and is followed by optional <em>\r
-                                       format modifiers</em> and a <em>conversion character</em>. The recognized conversion specifiers \r
-                                       are:</p>\r
+                               <p>A conversion specifier begins with a percent sign (%) and is followed by a <em>conversion word</em>.\r
+                               Some conversion words require one or more <em>options</em> to be given. These are specified in braces after the \r
+                               conversion word. An example of a conversion specifier is <code>%message</code> which will be converted into\r
+                               the message from the logging event which is being logged.</p>\r
+                               \r
+                               <p>The recognized conversion specifiers are:</p>\r
                                        \r
-                                       <table>\r
-                                               <thead>\r
-                                                       <tr>\r
-                                                               <th>Conversion character</th>\r
-                                                               <th>Converts to</th>\r
-                                                       </tr>\r
-                                               </thead>\r
-                                               <tbody>\r
-                                                       <tr>\r
-                                                               <td>\r
-                                                                       <p><code>%c</code></p>\r
-                                                                       <p><code>%c{<![CDATA[<precision>]]>}</code></p>\r
-                                                               </td>\r
-                                                               <td>\r
-                                                                       <p>Name of the Logger object which recieved the logging request.</p>\r
-                                                                       \r
-                                                                       <p>Optionally, it can be can followed by <em>precision specifier</em>, which is a \r
-                                                                       decimal constant in brackets. If a precision specifier is given, then only the \r
-                                                                       corresponding number of right most components of the logger name will be printed.</p>\r
-                                                                       \r
-                                                                       <p>For example, if the logger is named <code>foo.bar.Baz</code>, then <code>%c</code>\r
-                                                                       will be translated to the full logger name, <code>%c{2}</code> will be translated to\r
-                                                                       <code>bar.Baz</code>, and <code>%c{1}</code> will be translated to <code>Baz</code>.\r
-                                                                       </p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td>\r
-                                                                       <p><code>%C</code></p>\r
-                                                                       <p><code>%C{<![CDATA[<precision>]]>}</code></p>\r
-                                                               </td>\r
-                                                               <td>\r
-                                                                       <p>The fully qualified class name of the caller issuing the logging request. Currently,\r
-                                                                       this will always return "Logger".</p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td>\r
-                                                                       <p><code>%d</code></p>\r
-                                                                       <p><code>%d{<![CDATA[<format>]]>}</code></p>\r
-                                                               </td>\r
-                                                               <td>\r
-                                                                       <p>The date of the logging event.</p>\r
-                                                                       \r
-                                                                       <p>Optionally, may be followed by a <em>date format specifier</em> enclosed between \r
-                                                                       braces. The format specifier follows the PHP \r
-                                                                       <a href="http://php.net/manual/en/function.date.php">date</a> function. If no date \r
-                                                                       format specifier is given then ISO8601 format is assumed (Y-m-d H:i:s,u). </p>\r
-                                                                        \r
-                                                                       <p>For example: <code>%d{Y-m-d H:i:s}</code></p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%F</code></td>\r
-                                                               <td>Name of the file from which the logging request was issued.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%l</code></td>\r
-                                                               <td>\r
-                                                                       <p>Location information of the caller which generated the logging event.</p>\r
-                                                                       <p>Identical to <code>%C.%M(%F:%L)</code></p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%L</code></td>\r
-                                                               <td>The line number at which the logging request was issued.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%m</code></td>\r
-                                                               <td>The message associated with the logging event.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%n</code></td>\r
-                                                               <td>\r
-                                                                       <p>A platform dependent line-break character(s).</p>\r
-                                                                       <p>Note that a line break will not be printed unless explicitely specified.</p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%M</code></td>\r
-                                                               <td>The method or function name from which the logging request was issued.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%p</code></td>\r
-                                                               <td>The level of the logging event.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%r</code></td>\r
-                                                               <td>The number of milliseconds elapsed since the start of the application until the creation of the logging event.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%t</code></td>\r
-                                                               <td>The ID of the process that generated the logging event.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%x</code></td>\r
-                                                               <td>The NDC (Nested Diagnostic Context) associated with the thread that generated the logging event.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%X{<![CDATA[<key>]]>}</code></td>\r
-                                                               <td>\r
-                                                                       <p>The MDC (Mapped Diagnostic Context) associated with the thread that generated the \r
-                                                                       logging event.</p>\r
-                                                                       <p>The X conversion character must be followed by the MDC key in braces. The value in \r
-                                                                       the MDC corresponding to the key will be output.</p>\r
-                                                               </td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td><code>%%</code></td>\r
-                                                               <td>A single percent sign.</td> \r
-                                                       </tr>\r
-                                               </tbody>\r
-                                       </table>                        \r
-                               </subsection>\r
+                               <table class="table table-bordered">\r
+                                   <thead>\r
+                                   <tr>\r
+                                               <th>Conversion specifier</th>\r
+                                               <th>Converts to</th>\r
+                                   </tr>\r
+                                   </thead>\r
+                                   <tbody>\r
+                                       <tr>\r
+                                               <td>\r
+                                                   <p><strong>%c</strong>{length}</p>\r
+                                                       <p><strong>%lo</strong>{length}</p>\r
+                                                       <p><strong>%logger</strong>{length}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>Name of the Logger which generated the logging request.</p>\r
+                                                       \r
+                                                       <p>Optionally, a desired output length can be specified. If given, the converter will attempt\r
+                                                       to abbreviate the logger name without losing too much information in the process. If \r
+                                                       zero length is specified, only the rightmost name fragment will be output.</p>\r
+                                                       \r
+                                                       <p>Specifying the desired length 0 means that only the class name will be returned without\r
+                                                       the corresponding namespace.</p>\r
+                                                       \r
+                                                       <p>Several examples of the shortening algorithm in action:</p>\r
+                                                       \r
+                                                   <table class="table table-bordered table-not-wide">\r
+                                                               <thead>\r
+                                                                       <tr>\r
+                                                                               <th>Conversion specifier</th>\r
+                                                                               <th>Logger name</th>\r
+                                                                               <th>Result</th>\r
+                                                                       </tr>\r
+                                                               </thead>\r
+                                                               <tbody>\r
+                                                                       <tr>\r
+                                                                               <td>%logger</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%logger{0}</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>Foo</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%logger{10}</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>o\a\l\l\Foo</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%logger{20}</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>o\a\l\log4php\Foo</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%logger{25}</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>o\a\logging\log4php\Foo</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%logger{30}</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                               <td>org\apache\logging\log4php\Foo</td>\r
+                                                                       </tr>\r
+                                                               </tbody>\r
+                                                       </table>\r
+                                                       \r
+                                                       <p>Note that rightmost segment will never be shortened. It is possible that the \r
+                                                       resulting string will be longer than the specified desired length.</p>\r
+                                                       <p>For backward compatibility, a dot can be used as a namespace separator, as well as\r
+                                                       the backslash.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%C</strong>{length}</p>\r
+                                                       <p><strong>%class</strong>{length}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>The fully qualified class name of the caller issuing the logging request.</p>\r
+                                                       <p>Just like <strong>%logger</strong>, a desired length can be defined as an option.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%cookie</strong>{key}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A value from the $_COOKIE superglobal array corresponding to the given key.</p>\r
+                                                       <p>If no key is given, will return all values in key=value format.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%d</strong>{pattern}</p>\r
+                                                       <p><strong>%date</strong>{pattern}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>The date/time of the logging event. Accepts a pattern string as an option. The \r
+                                                       pattern syntax is the same as used by the PHP's <code><a href="http://php.net/manual/en/function.date.php" \r
+                                                       target="_blank" class="externalLink">date()</a></code> function.</p>\r
+                                                       \r
+                                                       <p>If no pattern is given, the date format will default to the ISO8601 datetime format,\r
+                                                       which is the same as giving the pattern: <code>c</code>.</p>\r
+                                                       \r
+                                                       <table>\r
+                                                               <thead>\r
+                                                                       <tr>\r
+                                                                               <th>Conversion specifier</th>\r
+                                                                               <th>Result</th>\r
+                                                                       </tr>\r
+                                                               </thead>\r
+                                                               <tbody>\r
+                                                                       <tr>\r
+                                                                               <td>%d</td>\r
+                                                                               <td>2011-12-27T12:01:32+01:00</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%date</td>\r
+                                                                               <td>2011-12-27T12:01:32+01:00</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%date{ISO8601}</td>\r
+                                                                               <td>2011-12-27T12:01:32+01:00</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%date{Y-m-d H:i:s,u}</td>\r
+                                                                               <td>2011-12-27 12:01:32,610</td>\r
+                                                                       </tr>\r
+                                                                       <tr>\r
+                                                                               <td>%date{l jS \of F Y h:i:s A}</td>\r
+                                                                               <td>Tuesday 27th of December 2011 12:01:32 PM</td>\r
+                                                                       </tr>\r
+                                                               </tbody>\r
+                                                       </table>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%e</strong>{key}</p>\r
+                                                       <p><strong>%env</strong>{key}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A value from the $_ENV superglobal array corresponding to the given key.</p>\r
+                                                       <p>If no key is given, will return all values in key=value format.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%F</strong></p>\r
+                                                       <p><strong>%file</strong></p>\r
+                                               </td>\r
+                                               <td>Name of the file from which the logging request was issued.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%l</strong></p>\r
+                                                       <p><strong>%location</strong></p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>Location information of the caller which generated the logging event.</p>\r
+                                                       <p>Identical to <code>%C.%M(%F:%L)</code></p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%L</strong></p>\r
+                                                       <p><strong>%line</strong></p>\r
+                                               </td>\r
+                                               <td>The line number at which the logging request was issued.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%m</strong></p>\r
+                                                       <p><strong>%msg</strong></p>\r
+                                                       <p><strong>%message</strong></p>\r
+                                               </td>\r
+                                               <td>The message associated with the logging event.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                   <p><strong>%n</strong></p>\r
+                                                       <p><strong>%newline</strong></p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A platform dependent line-break character(s).</p>\r
+                                                       <p>Note that a line break will not be printed unless explicitely specified.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%M</strong></p>\r
+                                                       <p><strong>%method</strong></p>\r
+                                               </td>\r
+                                               <td>The method or function name from which the logging request was issued.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%p</strong></p>\r
+                                                       <p><strong>%le</strong></p>\r
+                                                       <p><strong>%level</strong></p>\r
+                                               </td>\r
+                                               <td>The level of the logging event.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%r</strong></p>\r
+                                                       <p><strong>%relative</strong></p>\r
+                                               </td>\r
+                                               <td>The number of milliseconds elapsed since the start of the application until the creation of the logging event.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%req</strong>{key}</p>\r
+                                                       <p><strong>%request</strong>{key}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A value from the $_REQUEST superglobal array corresponding to the given key.</p>\r
+                                                       <p>If no key is given, will return all values in key=value format.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%s</strong>{key}</p>\r
+                                                       <p><strong>%server</strong>{key}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A value from the $_SERVER superglobal array corresponding to the given key.</p>\r
+                                                       <p>If no key is given, will return all values in key=value format.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%t</strong></p>\r
+                                                       <p><strong>%pid</strong></p>\r
+                                                       <p><strong>%process</strong></p>\r
+                                               </td>\r
+                                               <td>The ID of the process that generated the logging event.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%x</strong></p>\r
+                                                       <p><strong>%ndc</strong></p>\r
+                                               </td>\r
+                                               <td>The NDC (Nested Diagnostic Context) associated with the thread that generated the logging event.</td>\r
+                                       </tr>\r
+                                       <tr>\r
+                                               <td>\r
+                                                       <p><strong>%X</strong>{key}</p>\r
+                                                       <p><strong>%mdc</strong>{key}</p>\r
+                                               </td>\r
+                                               <td>\r
+                                                       <p>A value from the Mapped Diagnostic Context (MDC) corresponding to the given key.</p>\r
+                                               </td>\r
+                                       </tr>\r
+                    </tbody>\r
+                               </table>                        \r
+                       </subsection>\r
                \r
-                               <subsection name="Format modifiers">\r
-                                       <p>By default the relevant information is output as-is. However, with the aid of format modifiers \r
-                                       it is possible to change the minimum and maximum width and the justifications of each data field.\r
-                                       </p>\r
-                                       \r
-                                       <p>All format modifiers are optional, and are placed between the percent sign and the conversion \r
-                                       character.</p>\r
-                                       \r
-                                       <p>The first format modifier is the <em>left justification flag</em> which is just the minus (-)\r
-                                       character.</p>\r
-                                        \r
-                                       <p>Then comes the <em>>minimum field width</em> modifier. This is an integer that \r
-                                       represents the minimum number of characters to output. If the data item requires fewer characters,\r
-                                       it is padded on either the left or the right until the minimum width is reached. The default is to\r
-                                       pad on the left (right justify) but you can specify right padding with the left justification flag.\r
-                                       The padding character is space. If the data item is larger than the minimum field width, the field\r
-                                       is expanded to accommodate the data. The value is never truncated. </p>\r
-                                        \r
-                                       <p>This behavior can be changed using the <em>maximum field width</em> modifier which is designated\r
-                                       by a period (.) followed by an integer. If the data item is longer than the maximum field, then the\r
-                                       extra characters are removed from the beginning of the data item and not from the end. For example,\r
-                                       it the maximum field width is eight and the data item is ten characters long, then the first two \r
-                                       characters of the data item are dropped. This behavior deviates from the printf function in C where\r
-                                       truncation is done from the end. </p>\r
-                                       \r
-                                       <p>The following table demonstrates various uses of format modifiers:</p>\r
-                                       \r
-                                       <table>\r
-                                               <thead>\r
-                                                       <tr>\r
-                                                               <th>Format modifier</th>\r
-                                                               <th>Justification</th>\r
-                                                               <th>Minimum width</th>\r
-                                                               <th>Maximum width</th>\r
-                                                               <th>Comment</th>\r
-                                                       </tr>\r
-                                               </thead>\r
-                                               <tbody>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%c</code></td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td>Output the logger name as-is.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%20c</code></td>\r
-                                                               <td align="center">right</td>\r
-                                                               <td align="center">20</td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td>Left pad with spaces if the logger name is less than 20 characters long.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%-20c</code></td>\r
-                                                               <td align="center">left</td>\r
-                                                               <td align="center">20</td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td>Right pad with spaces if the logger name is less than 20 characters long.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%.30c</code></td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td align="center">none</td>\r
-                                                               <td align="center">30</td>\r
-                                                               <td>Truncate from the beginning if the logger name is longer than 30 characters.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%20.30c</code></td>\r
-                                                               <td align="center">right</td>\r
-                                                               <td align="center">20</td>\r
-                                                               <td align="center">30</td>\r
-                                                               <td>Left pad with spaces if the logger name is shorter than 20 characters. However, if \r
-                                                               the logger name is longer than 30 characters, then truncate from the beginning.</td>\r
-                                                       </tr>\r
-                                                       <tr>\r
-                                                               <td align="center"><code>%-20.30c</code></td>\r
-                                                               <td align="center">true</td>\r
-                                                               <td align="center">20</td>\r
-                                                               <td align="center">30</td>\r
-                                                               <td>Right pad with spaces if the logger name is shorter than 20 characters. However, if the\r
-                                                               logger name is longer than 30 characters, then truncate from the beginning.</td>\r
-                                                       </tr>\r
-                                               </tbody>\r
-                                       </table> \r
-                               </subsection>\r
+                       <subsection name="Format modifiers">\r
+                               <p>By default the relevant information is output as-is. However, with the aid of format modifiers \r
+                               it is possible to change the minimum and maximum width and the justifications of each data field.\r
+                               </p>\r
+                               \r
+                               <p>All format modifiers are optional, and are placed between the percent sign and the conversion \r
+                               word.</p>\r
+                               \r
+                               <p>The first format modifier is the <em>left justification flag</em> which is just the minus (-)\r
+                               character.</p>\r
+                                \r
+                               <p>Then comes the <em>>minimum field width</em> modifier. This is an integer that \r
+                               represents the minimum number of characters to output. If the data item requires fewer characters,\r
+                               it is padded on either the left or the right until the minimum width is reached. The default is to\r
+                               pad on the left (right justify) but you can specify right padding with the left justification flag.\r
+                               The padding character is space. If the data item is larger than the minimum field width, the field\r
+                               is expanded to accommodate the data. The value is never truncated. </p>\r
+                                \r
+                               <p>This behavior can be changed using the <em>maximum field width</em> modifier which is designated\r
+                               by a period (.) followed by an integer. If the data item is longer than the maximum field, then the\r
+                               extra characters are removed from the beginning of the data item and not from the end. For example,\r
+                               it the maximum field width is eight and the data item is ten characters long, then the first two \r
+                               characters of the data item are dropped. This behavior deviates from the printf function in C where\r
+                               truncation is done from the end. </p>\r
+                               \r
+                               <p>The following table demonstrates various uses of format modifiers:</p>\r
+                               \r
+                               <table>\r
+                                       <thead>\r
+                                               <tr>\r
+                                                       <th>Format modifier</th>\r
+                                                       <th>Padding</th>\r
+                                                       <th>Trimming</th>\r
+                                                       <th>Minimum width</th>\r
+                                                       <th>Maximum width</th>\r
+                                                       <th>Comment</th>\r
+                                               </tr>\r
+                                       </thead>\r
+                                       <tbody>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%logger</strong></td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td>Output the logger name as-is.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%20logger</strong></td>\r
+                                                       <td align="center">right</td>\r
+                                                       <td align="center">20</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td>Left pad with spaces if the logger name is less than 20 characters long.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%-20logger</strong></td>\r
+                                                       <td align="center">left</td>\r
+                                                       <td align="center">20</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td>Right pad with spaces if the logger name is less than 20 characters long.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%.30logger</strong></td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">30</td>\r
+                                                       <td>Trim from the beginning if the logger name is longer than 30 characters.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%.-30logger</strong></td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">none</td>\r
+                                                       <td align="center">30</td>\r
+                                                       <td>Trim from the end if the logger name is longer than 30 characters.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%20.30logger</strong></td>\r
+                                                       <td align="center">right</td>\r
+                                                       <td align="center">20</td>\r
+                                                       <td align="center">30</td>\r
+                                                       <td>Left pad with spaces if the logger name is shorter than 20 characters. However, if \r
+                                                       the logger name is longer than 30 characters, then trim from the beginning.</td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td align="center"><strong>%-20.30logger</strong></td>\r
+                                                       <td align="center">true</td>\r
+                                                       <td align="center">20</td>\r
+                                                       <td align="center">30</td>\r
+                                                       <td>Right pad with spaces if the logger name is shorter than 20 characters. However, if the\r
+                                                       logger name is longer than 30 characters, then trim from the beginning.</td>\r
+                                               </tr>\r
+                                       </tbody>\r
+                               </table>\r
+                               \r
+                               <p>The following table lists a couple of examples for using format modifiers. Note that the square\r
+                               brackets are added to the conversion pattern to delimit the output.</p>\r
+                               \r
+                               <table class="table table-bordered table-striped table-not-wide">\r
+                                       <thead>\r
+                                               <tr>\r
+                                                       <th>Conversion pattern</th>\r
+                                                       <th>Logger name</th>\r
+                                                       <th>Result</th>\r
+                                               </tr>\r
+                                       </thead>\r
+                                       <tbody>\r
+                                               <tr>\r
+                                                       <td>[%10.10logger]</td>\r
+                                                       <td>Foo</td>\r
+                                                       <td><code>[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Foo]</code></td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td>[%-10.10logger]</td>\r
+                                                       <td>Foo</td>\r
+                                                       <td><code>[Foo&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;]</code></td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td>[%10.10logger]</td>\r
+                                                       <td>org.apache.log4php.Foo</td>\r
+                                                       <td><code>[og4php.Foo]</code></td>\r
+                                               </tr>\r
+                                               <tr>\r
+                                                       <td>[%10.-10logger]</td>\r
+                                                       <td>org.apache.log4php.Foo</td>\r
+                                                       <td><code>[org.apache]</code></td>\r
+                                               </tr>\r
+                                       </tbody>\r
+                               </table>\r
+                       </subsection>\r
 \r
-                               <subsection name="Examples">\r
-                                       <p>The following configuration configures a <code>LoggerAppenderEcho</code> which uses the pattern\r
-                                       layout. All examples will use the same code and configuration, only the conversion pattern will \r
-                                       change.</p>\r
-                                       \r
-                                       <p>Save the configuration to a file called <code>layout_pattern.xml</code>.</p>\r
+                       <subsection name="Examples">\r
+                               <p>The following configuration configures a <code>LoggerAppenderEcho</code> which uses the pattern\r
+                               layout. All examples will use the same code and configuration, only the conversion pattern will \r
+                               change from example to example.</p>\r
+                               \r
+                               <div class="auto-tabs">\r
+                                       <ul>\r
+                                               <li>XML</li>\r
+                                               <li>PHP</li>\r
+                                       </ul>\r
        \r
-                                       <div class="auto-tabs">\r
-                                               <ul>\r
-                                                       <li>XML</li>\r
-                                                       <li>PHP</li>\r
-                                               </ul>\r
-               \r
-                                                       <div class="tab-content" >\r
-                                                               <div class="tab-pane">\r
+                                               <div class="tab-content" >\r
+                                                       <div class="tab-pane">\r
 <pre class="prettyprint linenums"><![CDATA[\r
 <configuration xmlns="http://logging.apache.org/log4php/">\r
     <appender name="default" class="LoggerAppenderEcho">\r
         <layout class="LoggerLayoutPattern">\r
-            <param name="conversionPattern" value="%d{Y-m-d H:i:s.u} %c %-5p %m%n" />\r
+            <param name="conversionPattern" value="%date %logger %-5level %msg%n" />\r
         </layout>\r
     </appender>\r
     <root>\r
     </root>\r
 </configuration>\r
 ]]></pre>\r
-                                                               </div>\r
-                                                               <div class="tab-pane">\r
+                                                       </div>\r
+                                                       <div class="tab-pane">\r
 <pre class="prettyprint linenums"><![CDATA[\r
 array(\r
     'appenders' => array(\r
@@ -303,7 +493,7 @@ array(
             'layout' => array(\r
                 'class' => 'LoggerLayoutPattern',\r
                 'params' => array(\r
-                    'conversionPattern' => '%d{Y-m-d H:i:s.u} %c %-5p %m%n'\r
+                    'conversionPattern' => '%date %logger %-5level %msg%n'\r
                 )\r
             )\r
         )\r
@@ -313,36 +503,72 @@ array(
     ),\r
 )\r
 ]]></pre>\r
-                                                       </div>\r
                                                </div>\r
                                        </div>\r
-                                       \r
+                               </div>\r
+                               \r
 \r
-                                       <p>Run the following code:</p>\r
+                               <p>Example code:</p>\r
 \r
 <pre class="prettyprint linenums">\r
-Logger::configure("layout_pattern.xml");\r
+Logger::configure("config.xml");\r
 $logger = Logger::getLogger('myLogger');\r
 $logger->info("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");\r
 $logger->debug("Donec a diam lectus.");\r
 $logger->warn("Sed sit amet ipsum mauris.");\r
 </pre>\r
 \r
-                                       <h4>Pattern example</h4>\r
-                                       <p>Conversion pattern: <code>%d %c %-5p %m%n</code></p>\r
+                               <h4>A simple example</h4>\r
                                \r
-                                       <p>Produces the following output:</p>\r
+                           <p>Conversion pattern: <code>%date %logger %-5level %msg%n</code></p>\r
+                       \r
+                               <p>Running the example code produces the following output:</p>\r
 \r
 <pre>\r
-2011-09-28 09:29:38,602 myLogger INFO  Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r
-2011-09-28 09:29:38,603 myLogger DEBUG Donec a diam lectus.\r
-2011-09-28 09:29:38,604 myLogger WARN  Sed sit amet ipsum mauris.\r
+2012-02-27T19:42:17+01:00 myLogger INFO  Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r
+2012-02-27T19:42:17+01:00 myLogger DEBUG Donec a diam lectus.\r
+2012-02-27T19:42:17+01:00 myLogger WARN  Sed sit amet ipsum mauris.\r
 </pre>\r
 \r
-                                       <p>In this example, <code>%d</code> produces the event datetime in default format \r
-                                       (<code>Y-m-d H:i:s,u</code>), and <code>%-5p</code> produces the event level right padded to 5 \r
-                                       characters. Since longest level name is 5 characters long, this ensures that the message always \r
-                                       starts at the same character position which improves log readability.</p>\r
+                           <p>In this example, <code>%date</code> is converted to the event datetime in default format \r
+                           (corresponding to the ISO-8601 specification), and <code>%-5level</code> produces the event \r
+                           level right padded to 5 characters. Since longest level name is 5 characters long, this \r
+                           ensures that the message always starts at the same character position which improves log \r
+                           readability.</p>\r
+                           \r
+                           <p>Notice that the newline between logging events (%n) has to be explicitely defined. Otherwise all \r
+                           logging events will be logged in the same line.</p>\r
+                           \r
+                           <h4>Formatting the date</h4>\r
+                           \r
+                           <p>The <code>%date</code> conversion word can take the desired date format as an option. For example, \r
+                           if you're European, the d.m.Y date format might be more familiar. Also, adding milliseconds.</p>\r
+                           \r
+                           <p>Conversion pattern: <code>%date{d.m.Y H:i:s,u} %logger %-5level %msg%n</code></p>\r
+                           \r
+                               <p>Running the example code produces the following output:</p>\r
+\r
+<pre>\r
+27.02.2012 20:14:41,624 myLogger INFO  Lorem ipsum dolor sit amet, consectetur adipiscing elit.\r
+27.02.2012 20:14:41,625 myLogger DEBUG Donec a diam lectus.\r
+27.02.2012 20:14:41,626 myLogger WARN  Sed sit amet ipsum mauris.\r
+</pre>\r
+                           \r
+                           <h4>Logging HTTP requests</h4>\r
+                           \r
+                           <p>If log4php is used to log HTTP requests, a pattern like this might be useful:</p>\r
+                           \r
+                           <p><code>%date [%pid] From:%server{REMOTE_ADDR}:%server{REMOTE_PORT} Request:[%request] Message: %msg%n</code></p>\r
+                           \r
+                           <p>Request <code>/test.php?foo=bar</code> it will produce the output similar to:</p>\r
+                                   \r
+<pre>\r
+2012-01-02T14:19:33+01:00 [22924] From:194.152.205.71:11257 Request:[foo=bar] Message: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+2012-01-02T14:19:33+01:00 [22924] From:194.152.205.71:11257 Request:[foo=bar] Message: Donec a diam lectus.
+2012-01-02T14:19:33+01:00 [22924] From:194.152.205.71:11257 Request:[foo=bar] Message: Sed sit amet ipsum mauris.\r
+</pre>
+                                   
+                               <p><code>%server{REMOTE_ADDR}</code> is equivalent to PHP code <code>$_SERVER['REMOTE_ADDR']</code>.</p>
                        </subsection>\r
                </section>\r
        </body>\r
index 3309009..e691371 100644 (file)
@@ -40,8 +40,8 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
        /** A pattern with a non-existant key. */\r
        private $pattern4 = "%-5p %c: %X{key_does_not_exist} %m";\r
        \r
-       /** A pattern with an empty key. */\r
-       private $pattern5 = "%-5p %c: %X{} %m";\r
+       /** A pattern without a key. */\r
+       private $pattern5 = "%-5p %c: %X %m";\r
        \r
        /** A pattern for testing values from $_ENV. */\r
        private $patternEnv = "%-5p %c: %X{env.TEST} %m";\r
@@ -76,31 +76,31 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
                \r
                self::assertSame($expected, $actual);\r
                \r
-               $event = new LoggerLoggingEvent("LoggerLayoutPattern", new Logger("TEST"), LoggerLevel::getLevelInfo(), "Test message");\r
+               $event = LoggerTestHelper::getInfoEvent("Test message");\r
 \r
                // Pattern with 1 key\r
                $actual = $this->formatEvent($event, $this->pattern1);\r
-               $expected = "INFO  TEST: valueofkey1 Test message";\r
+               $expected = "INFO  test: valueofkey1 Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Pattern with 2 keys\r
                $actual = $this->formatEvent($event, $this->pattern2);\r
-               $expected = "INFO  TEST: valueofkey1 valueofkey2 Test message";\r
+               $expected = "INFO  test: valueofkey1 valueofkey2 Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Pattern with 3 keys (one numeric)\r
                $actual = $this->formatEvent($event, $this->pattern3);\r
-               $expected = "INFO  TEST: valueofkey1 valueofkey2 valueofkey3 Test message";\r
+               $expected = "INFO  test: valueofkey1 valueofkey2 valueofkey3 Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Pattern with non-existant key\r
                $actual = $this->formatEvent($event, $this->pattern4);\r
-               $expected = "INFO  TEST:  Test message";\r
+               $expected = "INFO  test:  Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Pattern with an empty key\r
        $actual = $this->formatEvent($event, $this->pattern5);\r
-               $expected = "INFO  TEST:  Test message";\r
+               $expected = "INFO  test: key1=valueofkey1, key2=valueofkey2, 3=valueofkey3 Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Test key removal\r
@@ -110,7 +110,7 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
                \r
                // Pattern with 1 key, now removed\r
                $actual = $this->formatEvent($event, $this->pattern1);\r
-               $expected = "INFO  TEST:  Test message";\r
+               $expected = "INFO  test:  Test message";\r
                self::assertEquals($expected, $actual);\r
     }\r
     \r
@@ -125,9 +125,9 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
        self::assertEquals('abc', LoggerMDC::get('env.TEST'));\r
        \r
        // Test env variable in a pattern\r
-       $event = new LoggerLoggingEvent("LoggerLayoutPattern", new Logger("TEST"), LoggerLevel::getLevelInfo(), "Test message");\r
+       $event = LoggerTestHelper::getInfoEvent("Test message");\r
        $actual = $this->formatEvent($event, $this->patternEnv);\r
-               $expected = "INFO  TEST: abc Test message";\r
+               $expected = "INFO  test: abc Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Test reading a non-existant env variable\r
@@ -144,9 +144,9 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
        self::assertEquals($value, LoggerMDC::get('server.PHP_SELF'));\r
        \r
                // Test the server variable in a pattern\r
-       $event = new LoggerLoggingEvent("LoggerLayoutPattern", new Logger("TEST"), LoggerLevel::getLevelInfo(), "Test message");\r
+       $event = LoggerTestHelper::getInfoEvent("Test message");\r
        $actual = $this->formatEvent($event, $this->patternServer);\r
-               $expected = "INFO  TEST: $value Test message";\r
+               $expected = "INFO  test: $value Test message";\r
                self::assertEquals($expected, $actual);\r
                \r
                // Test reading a non-existant server variable\r
@@ -159,6 +159,7 @@ class LoggerMDCTest extends PHPUnit_Framework_TestCase {
        private function formatEvent($event, $pattern) {\r
                $layout = new LoggerLayoutPattern();\r
                $layout->setConversionPattern($pattern);\r
+               $layout->activateOptions();\r
                return $layout->format($event);\r
        }\r
 }\r
index 2602ebb..26c58f5 100644 (file)
 class LoggerTestHelper {\r
        \r
        /** Returns a test logging event with level set to TRACE. */\r
-       public static function getTraceEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelTrace(), $message);\r
+       public static function getTraceEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelTrace(), $message);\r
        }\r
        \r
        /** Returns a test logging event with level set to DEBUG. */\r
-       public static function getDebugEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelDebug(), $message);\r
+       public static function getDebugEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelDebug(), $message);\r
        }\r
        \r
        /** Returns a test logging event with level set to INFO. */\r
-       public static function getInfoEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelInfo(), $message);\r
+       public static function getInfoEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelInfo(), $message);\r
        }\r
        \r
        /** Returns a test logging event with level set to WARN. */\r
-       public static function getWarnEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelWarn(), $message);\r
+       public static function getWarnEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelWarn(), $message);\r
        }\r
        \r
        /** Returns a test logging event with level set to ERROR. */\r
-       public static function getErrorEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelError(), $message);\r
+       public static function getErrorEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelError(), $message);\r
        }\r
        \r
        /** Returns a test logging event with level set to FATAL. */\r
-       public static function getFatalEvent($message = 'test') {\r
-               return new LoggerLoggingEvent(__CLASS__, new Logger("test"), LoggerLevel::getLevelFatal(), $message);\r
+       public static function getFatalEvent($message = 'test', $logger = "test") {\r
+               return new LoggerLoggingEvent(__CLASS__, new Logger($logger), LoggerLevel::getLevelFatal(), $message);\r
        }\r
        \r
        /** \r
@@ -111,6 +111,28 @@ class LoggerTestHelper {
                        ),\r
                );\r
        }\r
+       \r
+       /** Returns a simple configuration with one echo appender using the pattern layout. */\r
+       public static function getEchoPatternConfig($pattern) {\r
+               return array(\r
+                       'threshold' => 'ALL',\r
+                       'rootLogger' => array(\r
+                               'level' => 'trace',\r
+                               'appenders' => array('default'),\r
+                       ),\r
+                       'appenders' => array(\r
+                               'default' => array(\r
+                                       'class' => 'LoggerAppenderEcho',\r
+                                       'layout' => array(\r
+                                               'class' => 'LoggerLayoutPattern',\r
+                                               'params' => array(\r
+                                                       'conversionPattern' => $pattern\r
+                                               )\r
+                                       ),\r
+                               ),\r
+                       ),\r
+               );\r
+       }\r
 }\r
 \r
 ?>
\ No newline at end of file
index 3ea78c9..d92afa0 100644 (file)
  * @group appenders
  */
 class LoggerAppenderPDOTest extends PHPUnit_Framework_TestCase {
-    const dsn = 'sqlite:../../../target/pdotest.sqlite';
-    const file = '../../../target/pdotest.sqlite';
-        
-    /** To start with an empty database for each single test. */
-    public function setUp() {
-        if(!extension_loaded('pdo_sqlite')) {
-            self::markTestSkipped("Please install 'pdo_sqlite' in order to run this test");
-        }
-
-        if (file_exists(self::file)) @unlink(self::file);
-    }
+       const dsn = 'sqlite:../../../target/pdotest.sqlite';
+       const file = '../../../target/pdotest.sqlite';
+       
+       public static function setUpBeforeClass() {
+               if(extension_loaded('pdo_sqlite')) {
+                       $drop = 'DROP TABLE IF EXISTS log4php_log;';
+                       $create = 'CREATE TABLE log4php_log (
+                               timestamp VARCHAR(256),
+                               logger VARCHAR(256),
+                               level VARCHAR(32),
+                               message VARCHAR(4000),
+                               thread INTEGER,
+                               file VARCHAR(255),
+                               line VARCHAR(10)
+                       );';
+                       
+                       $pdo = new PDO(self::dsn);
+                       $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+                       $pdo->exec($drop);
+                       $pdo->exec($create);
+               }
+       }
+       
+       
+       /** To start with an empty database for each single test. */
+       public function setUp() {
+               if(!extension_loaded('pdo_sqlite')) {
+                       self::markTestSkipped("Please install 'pdo_sqlite' in order to run this test");
+               }
+       }
 
-    /** Clean up after the last test was run. */
-    public static function tearDownAfterClass() {
-        if (file_exists(self::file)) @unlink(self::file);
-    }
-    
+       /** Clean up after the last test was run. */
+       public static function tearDownAfterClass() {
+               if (file_exists(self::file)) @unlink(self::file);
+       }
+       
        public function testRequiresLayout() {
                $appender = new LoggerAppenderPDO();
                self::assertFalse($appender->requiresLayout());
        }
 
-    /** Tests new-style logging using prepared statements and the default SQL definition. */
-    public function testSimpleWithDefaults() {
-        // Log event
-        $event = new LoggerLoggingEvent("LoggerAppenderPDOTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-        $appender = new LoggerAppenderPDO("myname");
-        $appender->setDSN(self::dsn);
-        $appender->activateOptions();
-        $appender->append($event);
-        $appender->close();
-
-        // Test the default pattern %d,%c,%p,%m,%t,%F,%L
-        $db = new PDO(self::dsn);
-        $query = "SELECT * FROM log4php_log";
-        $sth = $db->query($query);
-        $row = $sth->fetch(PDO::FETCH_NUM);
-        self::assertTrue(is_array($row), "No rows found.");
-        self::assertEquals(7, count($row));
-        self::assertEquals(1, preg_match('/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d,\d\d\d$/', $row[0])); // %d = date
-        self::assertEquals('TEST', $row[1]); // %c = category
-        self::assertEquals('ERROR', $row[2]); // %p = priority
-        self::assertEquals('testmessage', $row[3]); // %m = message
-        if (function_exists('posix_getpid')) {
-            self::assertEquals(posix_getpid(), $row[4]); // %t = thread
-        }
-        self::assertEquals('NA', $row[5]); // %F = file, NA due to phpunit magic
-        self::assertEquals('NA', $row[6]); // %L = line, NA due to phpunit magic
-    }
-
+       /** Tests new-style logging using prepared statements and the default SQL definition. */
+       public function testSimpleWithDefaults() {
+               // Log event
+               $event = new LoggerLoggingEvent("LoggerAppenderPDOTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
+               $appender = new LoggerAppenderPDO("myname");
+               $appender->setDSN(self::dsn);
+               $appender->activateOptions();
+               $appender->append($event);
+               $appender->close();
 
-    /** Tests new style prepared statment logging with customized SQL. */
-    public function testCustomizedSql() {
-        // Prepare appender
-        $appender = new LoggerAppenderPDO("myname");
-        $appender->setDSN(self::dsn);
-        $appender->setTable('unittest2');
-        $appender->setInsertSql("INSERT INTO unittest2 (file, line, thread, timestamp, logger, level, message) VALUES (?,?,?,?,?,?,?)");
-        $appender->setInsertPattern("%F,%L,%t,%d,%c,%p,%m");
-        $appender->activateOptions();
-
-        // Action!
-        $event = new LoggerLoggingEvent("LoggerAppenderPDOTest2", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-        $appender->append($event);
-        
-        // Check
-        $db = new PDO(self::dsn);
-        $result = $db->query("SELECT * FROM unittest2");
-        $row = $result->fetch(PDO::FETCH_OBJ);
-        self::assertTrue(is_object($row));
-        self::assertEquals("NA", $row->file); // "NA" due to phpunit magic
-        self::assertEquals("NA", $row->line); // "NA" due to phpunit magic
-        if (function_exists('posix_getpid')) {
-            self::assertEquals(posix_getpid(), $row->thread);
-        }
-        self::assertEquals(1, preg_match('/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d.\d\d\d$/', $row->timestamp));
-        self::assertEquals('TEST', $row->logger);
-        self::assertEquals('ERROR', $row->level);
-        self::assertEquals('testmessage', $row->message);
-    }
-        
-    /** Tests old-style logging using the $sql variable. */
-    public function testOldStyle() {
-        // Create table with different column order
-        $db = new PDO(self::dsn);
-        $db->exec('CREATE TABLE unittest3 (ts timestamp, level varchar(32), msg varchar(64))');
+               // Test the default pattern
+               $db = new PDO(self::dsn);
+               $query = "SELECT * FROM log4php_log";
+               $sth = $db->query($query);
+               $row = $sth->fetch(PDO::FETCH_NUM);
+               
+               self::assertTrue(is_array($row), "No rows found.");
+               self::assertEquals(7, count($row));
+               self::assertEquals(1, preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $row[0])); // %d = date
+               self::assertEquals('TEST', $row[1]); // %c = category
+               self::assertEquals('ERROR', $row[2]); // %p = priority
+               self::assertEquals('testmessage', $row[3]); // %m = message
+               if (function_exists('posix_getpid')) {
+                       self::assertEquals(posix_getpid(), $row[4]); // %t = thread
+               }
+               self::assertEquals('NA', $row[5]); // %F = file, NA due to phpunit magic
+               self::assertEquals('NA', $row[6]); // %L = line, NA due to phpunit magic
+       }
 
-        // Prepare appender
-        $appender = new LoggerAppenderPDO("myname");
-        $appender->setDSN(self::dsn);
-        $appender->setCreateTable(false);
-        $appender->setSql("INSERT INTO unittest3 (ts, level, msg) VALUES ('%d', '%p', '%m')");
-        $appender->activateOptions();
 
-        // Action!
-        $event = new LoggerLoggingEvent("LoggerAppenderPDOTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-        $appender->append($event);
+       /** Tests new style prepared statment logging with customized SQL. */
+       public function testCustomizedSql() {
+               
+               $dateFormat = "Y-m-d H:i:s";
+               
+               // Prepare appender
+               $appender = new LoggerAppenderPDO("myname");
+               $appender->setDSN(self::dsn);
+               $appender->setInsertSql("INSERT INTO log4php_log (file, line, thread, timestamp, logger, level, message) VALUES (?,?,?,?,?,?,?)");
+               $appender->setInsertPattern("%F,%L,%t,%d\{$dateFormat\},%c,%p,%m");
+               $appender->activateOptions();
 
-        // Check
-        $db = new PDO(self::dsn);
-        $result = $db->query("SELECT * FROM unittest3");
-        self::assertFalse($result === false);
-        $row = $result->fetch(PDO::FETCH_OBJ);
-        self::assertTrue(is_object($row));
-        self::assertEquals(1, preg_match('/^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d.\d\d\d$/', $row->ts));
-        self::assertEquals('ERROR', $row->level);
-        self::assertEquals('testmessage', $row->msg);
-    }
-    
-    /** Tests if log4php throws an Exception if the appender does not work. 
-     * @expectedException LoggerException
-     */
-    public function testException() {
-        $dsn = 'doenotexist';
-        $appender = new LoggerAppenderPDO("myname");
-        $appender->setDSN($dsn);
-        $appender->setCreateTable(true);
-            $appender->activateOptions();
-    }
-    
+               // Action!
+               $event = new LoggerLoggingEvent("LoggerAppenderPDOTest2", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
+               $appender->append($event);
+               
+               $date = date($dateFormat, $event->getTimeStamp());
+               
+               // Check
+               $db = new PDO(self::dsn);
+               $result = $db->query("SELECT * FROM log4php_log");
+               $row = $result->fetch(PDO::FETCH_OBJ);
+               self::assertTrue(is_object($row));
+               self::assertEquals("NA", $row->file); // "NA" due to phpunit magic
+               self::assertEquals("NA", $row->line); // "NA" due to phpunit magic
+               if (function_exists('posix_getpid')) {
+                       self::assertEquals(posix_getpid(), $row->thread);
+               }
+               self::assertEquals($date, $row->timestamp);
+               self::assertEquals('TEST', $row->logger);
+               self::assertEquals('ERROR', $row->level);
+               self::assertEquals('testmessage', $row->message);
+       }
+       
+       /** 
+        * Tests a warning is shown when connecting to invalid dns. 
+        * @expectedException PHPUnit_Framework_Error
+        * @expectedExceptionMessage Failed connecting to database: invalid data source name
+        */
+       public function testException() {
+               $dsn = 'doenotexist';
+               $appender = new LoggerAppenderPDO("myname");
+               $appender->setDSN($dsn);
+               $appender->activateOptions();
+       }
+       
        /**
         * Check whether close() actually closes the database connection. 
         */
-    public function testClose() {
-       $event = new LoggerLoggingEvent("LoggerAppenderPDOTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-       
-        $appender = new LoggerAppenderPDO("myname");
-        $appender->setDSN(self::dsn);
-        $appender->activateOptions();
-        $appender->append($event);
-        $appender->close();
-        
-        self::assertNull($appender->getDatabaseHandle());
-    }
+       public function testClose() {
+               $event = new LoggerLoggingEvent("LoggerAppenderPDOTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
+               
+               $appender = new LoggerAppenderPDO("myname");
+               $appender->setDSN(self::dsn);
+               $appender->activateOptions();
+               $appender->append($event);
+               $appender->close();
+               
+               self::assertNull($appender->getDatabaseHandle());
+       }
 }
index fbeb11d..3fad1cf 100644 (file)
@@ -24,6 +24,9 @@
 
 error_reporting(E_ALL | E_STRICT); 
 
+// Required for testing logging of sessionID in pattern layout
+session_start();
+
 date_default_timezone_set('Europe/London');
 
 // Define a temp dir where tests may write to
index 6baf126..c797aba 100644 (file)
 class LoggerPatternParserTest extends PHPUnit_Framework_TestCase {
         
     public function testErrorLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-               $expected = 'ERROR TEST : testmessage in NA at NA'.PHP_EOL;
+//             $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
+//             $expected = 'ERROR TEST : testmessage in NA at NA'.PHP_EOL;
                
-               $patternParser = new LoggerPatternParser("%-5p %c %X{username}: %m in %F at %L%n");
-               $c = $patternParser->parse();
+//             $patternParser = new LoggerPatternParser("%-5p %c %X{username}: %m in %F at %L%n");
+//             $c = $patternParser->parse();
                
-               $actual = '';
-               $c->format($actual, $event);
+//             $actual = '';
+//             $c->format($actual, $event);
 //             self::assertEquals($expected, $actual);
 
     }
     
     public function testClassname() {
-               $event = new LoggerLoggingEvent("MyClass", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-               $expected = 'MyClass';
-               $patternParser = new LoggerPatternParser("%C");
-               $c = $patternParser->parse();
-               $actual = '';
-               $c->format($actual, $event);
-               self::assertEquals($expected, $actual);
+//             $event = new LoggerLoggingEvent("MyClass", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
+//             $expected = 'MyClass';
+//             $patternParser = new LoggerPatternParser("%C");
+//             $c = $patternParser->parse();
+//             $actual = '';
+//             $c->format($actual, $event);
+//             self::assertEquals($expected, $actual);
     }
 }
diff --git a/src/test/php/helpers/LoggerUtilsTest.php b/src/test/php/helpers/LoggerUtilsTest.php
new file mode 100644 (file)
index 0000000..0ed50e5
--- /dev/null
@@ -0,0 +1,88 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @category   tests\r
+ * @package    log4php\r
+ * @subpackage helpers\r
+ * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0\r
+ * @version    SVN: $Id$\r
+ * @link       http://logging.apache.org/log4php\r
+ */\r
+\r
+/**\r
+ * @group helpers\r
+ */\r
+class LoggerUtilsTest extends PHPUnit_Framework_TestCase {\r
+       \r
+       public function testShorten() {\r
+               $name = 'org\\apache\\logging\\log4php\\Foo';\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, null);\r
+               self::assertSame($name, $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 0);\r
+               self::assertSame('Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 5);\r
+               self::assertSame('o\\a\\l\\l\\Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 16);\r
+               self::assertSame('o\\a\\l\\l\\Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 17);\r
+               self::assertSame('o\\a\\l\\log4php\\Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 25);\r
+               self::assertSame('o\\a\\logging\\log4php\\Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 28);\r
+               self::assertSame('o\\apache\\logging\\log4php\\Foo', $actual);\r
+               \r
+               $actual = LoggerUtils::shortenClassName($name, 30);\r
+               self::assertSame('org\\apache\\logging\\log4php\\Foo', $actual);\r
+       }\r
+       \r
+       /** Dot separated notation must be supported for legacy reasons. */\r
+       public function testShortenWithDots() {\r
+               $name = 'org.apache.logging.log4php.Foo';\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, null);\r
+               self::assertSame($name, $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 0);\r
+               self::assertSame('Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 5);\r
+               self::assertSame('o\a\l\l\Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 16);\r
+               self::assertSame('o\a\l\l\Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 17);\r
+               self::assertSame('o\a\l\log4php\Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 25);\r
+               self::assertSame('o\a\logging\log4php\Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 28);\r
+               self::assertSame('o\apache\logging\log4php\Foo', $actual);\r
+       \r
+               $actual = LoggerUtils::shortenClassName($name, 30);\r
+               self::assertSame('org\apache\logging\log4php\Foo', $actual);\r
+       }       \r
+       \r
+}
\ No newline at end of file
index 8205b7c..0d60f5d 100644 (file)
 class LoggerLayoutPatternTest extends PHPUnit_Framework_TestCase {
 
        /** Pattern used for testing. */
-       private $pattern = "%d{Y-m-d H:i:s.u} %-5p %c (%C): %m in %F at %L%n";
+       private $pattern = "%-6level %logger: %msg from %class::%method() in %file at %line%n";
        
-       public function testErrorLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-
-               $v = $this->formatEvent($event, $this->pattern);
-               $dt = $this->getEventDateTime($event);
-               $e = "$dt ERROR TEST (LoggerLayoutXml): testmessage in NA at NA".PHP_EOL;
-               self::assertEquals($e, $v);
-    }
-    
-    public function testWarnLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelWarn(), "testmessage");
-
-               $v = $this->formatEvent($event, $this->pattern);
-               $dt = $this->getEventDateTime($event);
-               $e = "$dt WARN  TEST (LoggerLayoutXml): testmessage in NA at NA".PHP_EOL;
-               self::assertEquals($e, $v);
-    }
-    
-    public function testInfoLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelInfo(), "testmessage");
-
-               $v = $this->formatEvent($event, $this->pattern);
-               $dt = $this->getEventDateTime($event);
-               $e = "$dt INFO  TEST (LoggerLayoutXml): testmessage in NA at NA".PHP_EOL;
-               self::assertEquals($e, $v);
-    }
-    
-    public function testDebugLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml", new Logger("TEST"), LoggerLevel::getLevelDebug(), "testmessage");
-
-               $v = $this->formatEvent($event, $this->pattern);
-               $dt = $this->getEventDateTime($event);
-               $e = "$dt DEBUG TEST (LoggerLayoutXml): testmessage in NA at NA".PHP_EOL;
-               self::assertEquals($e, $v);
-    }
-    
-    public function testTraceLayout() {
-               $event = new LoggerLoggingEvent("LoggerLayoutXml2", new Logger("TEST"), LoggerLevel::getLevelTrace(), "testmessage");
+       public function testComplexLayout() {
                
-               $v = $this->formatEvent($event, $this->pattern);
-               $dt = $this->getEventDateTime($event);
-               $e = "$dt TRACE TEST (LoggerLayoutXml2): testmessage in NA at NA".PHP_EOL;
-               self::assertEquals($e, $v);
-    }
-    
-    public function testClassnamePattern() {
-               $event = new LoggerLoggingEvent("LoggerLayoutPatternTest", new Logger("TEST"), LoggerLevel::getLevelError(), "testmessage");
-
-               $v = $this->formatEvent($event, '%C');
+               $config = LoggerTestHelper::getEchoPatternConfig($this->pattern);
+               Logger::configure($config);
                
-               $dt = $this->getEventDateTime($event);
-               $e = "LoggerLayoutPatternTest";
-       
-               self::assertEquals($e, $v);
-    }
-    
-    /** 
-     *  Returns the datetime of an event in "Y-m-d H:i:s.u" format. This is required because
-     *  the PHP date() function does not handle the microseconds on windows (returns zeros).  
-     *  
-     *  @see http://www.php.net/manual/en/function.date.php#93752
-     */
-    private function getEventDateTime($event) {
-       
-               $ts = $event->getTimeStamp();
-               $micros = round(($ts - floor($ts)) * 1000); // microseconds to 3 decimal places
-               $micros = str_pad($micros, 3, '0', STR_PAD_LEFT);
-               return date('Y-m-d H:i:s', $ts).".$micros";
+               ob_start();
+               $log = Logger::getLogger('LoggerTest');
+               $log->error("my message"); $line = __LINE__;
+               $actual = ob_get_contents();
+               ob_end_clean();
+               
+               $file = __FILE__;
+               $class = __CLASS__;
+               $method = __FUNCTION__;
+               
+               $expected = "ERROR  LoggerTest: my message from $class::$method() in $file at $line" . PHP_EOL;
+               self::assertSame($expected, $actual);
+               
+               Logger::resetConfiguration();
     }
-    
-       private function formatEvent($event, $pattern) {
-               $layout = new LoggerLayoutPattern();
-               $layout->setConversionPattern($pattern);
-               return $layout->format($event);
-       }
-    
 }
diff --git a/src/test/php/pattern/LoggerPatternConverterTest.php b/src/test/php/pattern/LoggerPatternConverterTest.php
new file mode 100644 (file)
index 0000000..46190a3
--- /dev/null
@@ -0,0 +1,357 @@
+<?php\r
+/**\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements.  See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License.  You may obtain a copy of the License at\r
+ *\r
+ *      http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ *\r
+ * @category   tests\r
+ * @package    log4php\r
+ * @subpackage filters\r
+ * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0\r
+ * @version    SVN: $Id$\r
+ * @link       http://logging.apache.org/log4php\r
+ */\r
+\r
+/** Converter referencing non-existant superglobal variable. */\r
+class LoggerInvalidSuperglobalConverter extends LoggerPatternConverterSuperglobal {\r
+       protected $name = '_FOO';\r
+}\r
+\r
+/**\r
+ * @group pattern\r
+ */\r
+class LoggerPatternConverterTest extends PHPUnit_Framework_TestCase {\r
+\r
+       /**\r
+        * A logging event for testing.\r
+        * @var LoggerLoggingEvent\r
+        */\r
+       private $event;\r
+\r
+       /**\r
+        * Fromatting info used with the logging event.\r
+        * @var LoggerFormattingInfos\r
+        */\r
+       private $info;\r
+\r
+       public function __construct() {\r
+               $this->event = LoggerTestHelper::getInfoEvent('foobar');\r
+               $this->info = new LoggerFormattingInfo();\r
+       }\r
+\r
+       public function testCookie() {\r
+               // Fake a couple of cookies\r
+               $_COOKIE['test1'] = 'value1';\r
+               $_COOKIE['test2'] = 'value2';\r
+\r
+               $converter = new LoggerPatternConverterCookie($this->info, 'test1');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = 'value1';\r
+               self::assertSame($expected, $actual);\r
+\r
+               $converter = new LoggerPatternConverterCookie($this->info, 'test2');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = 'value2';\r
+               self::assertSame($expected, $actual);\r
+\r
+               $converter = new LoggerPatternConverterCookie($this->info);\r
+               $actual = $converter->convert($this->event);\r
+               $expected = "test1=value1, test2=value2";\r
+               self::assertSame($expected, $actual);\r
+       }\r
+\r
+       public function testDate() {\r
+               $converter = new LoggerPatternConverterDate($this->info, 'c');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = date('c', $this->event->getTimeStamp());\r
+               self::assertSame($expected, $actual);\r
+\r
+               // Format defaults to 'c'\r
+               $converter = new LoggerPatternConverterDate($this->info);\r
+               $actual = $converter->convert($this->event);\r
+               $expected = date('c', $this->event->getTimeStamp());\r
+               self::assertSame($expected, $actual);\r
+\r
+               // Test ABSOLUTE\r
+               $converter = new LoggerPatternConverterDate($this->info, 'ABSOLUTE');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = date('H:i:s', $this->event->getTimeStamp());\r
+               self::assertSame($expected, $actual);\r
+\r
+               // Test DATE\r
+               $converter = new LoggerPatternConverterDate($this->info, 'DATE');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = date('d M Y H:i:s.', $this->event->getTimeStamp());\r
+\r
+               $timestamp = $this->event->getTimeStamp();\r
+               $ms = floor(($timestamp - floor($timestamp)) * 1000);\r
+               $ms = str_pad($ms, 3, '0', STR_PAD_LEFT);\r
+\r
+               $expected .= $ms;\r
+\r
+               self::assertSame($expected, $actual);\r
+       }\r
+\r
+       public function testEnvironment() {\r
+               // Fake a couple of environment values\r
+               $_ENV['test1'] = 'value1';\r
+               $_ENV['test2'] = 'value2';\r
+\r
+               $converter = new LoggerPatternConverterEnvironment($this->info, 'test1');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = 'value1';\r
+               self::assertSame($expected, $actual);\r
+\r
+               $converter = new LoggerPatternConverterEnvironment($this->info, 'test2');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = 'value2';\r
+               self::assertSame($expected, $actual);\r
+       }\r
+\r
+       public function testLevel() {\r
+               $converter = new LoggerPatternConverterLevel($this->info);\r
+               $actual = $converter->convert($this->event);\r
+               $expected = $this->event->getLevel()->toString();\r
+               self::assertEquals($expected, $actual);\r
+       }\r
+\r
+       public function testLiteral() {\r
+               $converter = new LoggerPatternConverterLiteral('foo bar baz');\r
+               $actual = $converter->convert($this->event);\r
+               $expected = 'foo bar baz';\r
+               self::assertEquals($expected, $actual);\r
+       }\r
+\r
+       public function testLoggerWithoutOption() {\r
+               $event = LoggerTestHelper::getInfoEvent('foo', 'TestLoggerName');\r
+               $converter = new LoggerPatternConverterLogger($this->info);\r
+\r
+               $actual = $converter->convert($event);\r
+               $expected = 'TestLoggerName';\r
+               self::assertEquals($expected, $actual);\r
+       }\r
+\r
+       public function testLoggerWithOption0() {\r
+               $event = LoggerTestHelper::getInfoEvent('foo', 'TestLoggerName');\r
+               $converter = new LoggerPatternConverterLogger($this->info, '0');\r
+\r
+               $actual = $converter->convert($event);\r
+               $expected = 'TestLoggerName';\r
+               self::assertEquals($expected, $actual);\r
+       }\r
+\r
+       public function testLocation() {\r
+               $config = LoggerTestHelper::getEchoPatternConfig("%file:%line:%class:%method");\r
+               Logger::configure($config);\r
+\r
+               // Test by capturing output. Logging methods of a Logger object must\r
+               // be used for the location info to be formed correctly.\r
+               ob_start();\r
+               $log = Logger::getLogger('foo');\r
+               $log->info('foo'); $line = __LINE__; // Do NOT move this to next line.\r
+               $actual = ob_get_contents();\r
+               ob_end_clean();\r
+\r
+               $expected = implode(':', array(__FILE__, $line, __CLASS__, __FUNCTION__));\r
+               self::assertSame($expected, $actual);\r
+\r
+               Logger::resetConfiguration();\r
+       }\r
+\r
+    &nb