library/log4php/configurators/LoggerConfiguratorDefault.php
changeset 46 f11c31f7fa3e
parent 45 a56e7f9a0463
child 47 03388ec805b4
equal deleted inserted replaced
45:a56e7f9a0463 46:f11c31f7fa3e
     1 <?php
       
     2 /**
       
     3  * Licensed to the Apache Software Foundation (ASF) under one or more
       
     4  * contributor license agreements. See the NOTICE file distributed with
       
     5  * this work for additional information regarding copyright ownership.
       
     6  * The ASF licenses this file to You under the Apache License, Version 2.0
       
     7  * (the "License"); you may not use this file except in compliance with
       
     8  * the License. You may obtain a copy of the License at
       
     9  *
       
    10  *       http://www.apache.org/licenses/LICENSE-2.0
       
    11  *
       
    12  * Unless required by applicable law or agreed to in writing, software
       
    13  * distributed under the License is distributed on an "AS IS" BASIS,
       
    14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    15  * See the License for the specific language governing permissions and
       
    16  * limitations under the License.
       
    17  *
       
    18  * @package log4php
       
    19  */
       
    20 
       
    21 /**
       
    22  * Default implementation of the logger configurator.
       
    23  *
       
    24  * Configures log4php based on a provided configuration file or array.
       
    25  *
       
    26  * @package log4php
       
    27  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
       
    28  * @version $Revision: 1394956 $
       
    29  * @since 2.2
       
    30  */
       
    31 class LoggerConfiguratorDefault implements LoggerConfigurator {
       
    32     /** XML configuration file format. */
       
    33     const FORMAT_XML = 'xml';
       
    34 
       
    35     /** PHP configuration file format. */
       
    36     const FORMAT_PHP = 'php';
       
    37 
       
    38     /** INI (properties) configuration file format. */
       
    39     const FORMAT_INI = 'ini';
       
    40 
       
    41     /** Defines which adapter should be used for parsing which format. */
       
    42     private $adapters = array(
       
    43         self::FORMAT_XML => 'LoggerConfigurationAdapterXML',
       
    44         self::FORMAT_INI => 'LoggerConfigurationAdapterINI',
       
    45         self::FORMAT_PHP => 'LoggerConfigurationAdapterPHP',
       
    46     );
       
    47 
       
    48     /** Default configuration; used if no configuration file is provided. */
       
    49     private static $defaultConfiguration = array(
       
    50         'threshold' => 'ALL',
       
    51         'rootLogger' => array(
       
    52             'level' => 'DEBUG',
       
    53             'appenders' => array('default'),
       
    54         ),
       
    55         'appenders' => array(
       
    56             'default' => array(
       
    57                 'class' => 'LoggerAppenderEcho'
       
    58             ),
       
    59         ),
       
    60     );
       
    61 
       
    62     /** Holds the appenders before they are linked to loggers. */
       
    63     private $appenders = array();
       
    64 
       
    65     /**
       
    66      * Configures log4php based on the given configuration. The input can
       
    67      * either be a path to the config file, or a PHP array holding the
       
    68      * configuration.
       
    69      *
       
    70      * If no configuration is given, or if the given configuration cannot be
       
    71      * parsed for whatever reason, a warning will be issued, and log4php
       
    72      * will use the default configuration contained in
       
    73      * {@link $defaultConfiguration}.
       
    74      *
       
    75      * @param LoggerHierarchy $hierarchy The hierarchy on which to perform
       
    76      *        the configuration.
       
    77      * @param string|array $input Either path to the config file or the
       
    78      *        configuration as an array. If not set, default configuration
       
    79      *        will be used.
       
    80      */
       
    81     public function configure(LoggerHierarchy $hierarchy, $input = null) {
       
    82         $config = $this->parse($input);
       
    83         $this->doConfigure($hierarchy, $config);
       
    84     }
       
    85 
       
    86     /**
       
    87      * Parses the given configuration and returns the parsed configuration
       
    88      * as a PHP array. Does not perform any configuration.
       
    89      *
       
    90      * If no configuration is given, or if the given configuration cannot be
       
    91      * parsed for whatever reason, a warning will be issued, and the default
       
    92      * configuration will be returned ({@link $defaultConfiguration}).
       
    93      *
       
    94      * @param string|array $input Either path to the config file or the
       
    95      *        configuration as an array. If not set, default configuration
       
    96      *        will be used.
       
    97      * @return array The parsed configuration.
       
    98      */
       
    99     public function parse($input) {
       
   100         // No input - use default configuration
       
   101         if (!isset($input)) {
       
   102             $config = self::$defaultConfiguration;
       
   103         } // Array input - contains configuration within the array
       
   104         else if (is_array($input)) {
       
   105             $config = $input;
       
   106         } // String input - contains path to configuration file
       
   107         else if (is_string($input)) {
       
   108             try {
       
   109                 $config = $this->parseFile($input);
       
   110             } catch (LoggerException $e) {
       
   111                 $this->warn("Configuration failed. " . $e->getMessage() . " Using default configuration.");
       
   112                 $config = self::$defaultConfiguration;
       
   113             }
       
   114         } // Anything else is an error
       
   115         else {
       
   116             $this->warn("Invalid configuration param given. Reverting to default configuration.");
       
   117             $config = self::$defaultConfiguration;
       
   118         }
       
   119 
       
   120         return $config;
       
   121     }
       
   122 
       
   123     /**
       
   124      * Returns the default log4php configuration.
       
   125      * @return array
       
   126      */
       
   127     public static function getDefaultConfiguration() {
       
   128         return self::$defaultConfiguration;
       
   129     }
       
   130 
       
   131     /**
       
   132      * Loads the configuration file from the given URL, determines which
       
   133      * adapter to use, converts the configuration to a PHP array and
       
   134      * returns it.
       
   135      *
       
   136      * @param string $url Path to the config file.
       
   137      * @return The configuration from the config file, as a PHP array.
       
   138      * @throws LoggerException If the configuration file cannot be loaded, or
       
   139      *        if the parsing fails.
       
   140      */
       
   141     private function parseFile($url) {
       
   142 
       
   143         if (!file_exists($url)) {
       
   144             throw new LoggerException("File not found at [$url].");
       
   145         }
       
   146 
       
   147         $type = $this->getConfigType($url);
       
   148         $adapterClass = $this->adapters[$type];
       
   149 
       
   150         $adapter = new $adapterClass();
       
   151         return $adapter->convert($url);
       
   152     }
       
   153 
       
   154     /** Determines configuration file type based on the file extension. */
       
   155     private function getConfigType($url) {
       
   156         $info = pathinfo($url);
       
   157         $ext = strtolower($info['extension']);
       
   158 
       
   159         switch ($ext) {
       
   160             case 'xml':
       
   161                 return self::FORMAT_XML;
       
   162 
       
   163             case 'ini':
       
   164             case 'properties':
       
   165                 return self::FORMAT_INI;
       
   166 
       
   167             case 'php':
       
   168                 return self::FORMAT_PHP;
       
   169 
       
   170             default:
       
   171                 throw new LoggerException("Unsupported configuration file extension: $ext");
       
   172         }
       
   173     }
       
   174 
       
   175     /**
       
   176      * Constructs the logger hierarchy based on configuration.
       
   177      *
       
   178      * @param LoggerHierarchy $hierarchy
       
   179      * @param array $config
       
   180      */
       
   181     private function doConfigure(LoggerHierarchy $hierarchy, $config) {
       
   182         if (isset($config['threshold'])) {
       
   183             $threshold = LoggerLevel::toLevel($config['threshold']);
       
   184             if (isset($threshold)) {
       
   185                 $hierarchy->setThreshold($threshold);
       
   186             } else {
       
   187                 $this->warn("Invalid threshold value [{$config['threshold']}] specified. Ignoring threshold definition.");
       
   188             }
       
   189         }
       
   190 
       
   191         // Configure appenders and add them to the appender pool
       
   192         if (isset($config['appenders']) && is_array($config['appenders'])) {
       
   193             foreach ($config['appenders'] as $name => $appenderConfig) {
       
   194                 $this->configureAppender($name, $appenderConfig);
       
   195             }
       
   196         }
       
   197 
       
   198         // Configure root logger
       
   199         if (isset($config['rootLogger'])) {
       
   200             $this->configureRootLogger($hierarchy, $config['rootLogger']);
       
   201         }
       
   202 
       
   203         // Configure loggers
       
   204         if (isset($config['loggers']) && is_array($config['loggers'])) {
       
   205             foreach ($config['loggers'] as $loggerName => $loggerConfig) {
       
   206                 $this->configureOtherLogger($hierarchy, $loggerName, $loggerConfig);
       
   207             }
       
   208         }
       
   209 
       
   210         // Configure renderers
       
   211         if (isset($config['renderers']) && is_array($config['renderers'])) {
       
   212             foreach ($config['renderers'] as $rendererConfig) {
       
   213                 $this->configureRenderer($hierarchy, $rendererConfig);
       
   214             }
       
   215         }
       
   216 
       
   217         if (isset($config['defaultRenderer'])) {
       
   218             $this->configureDefaultRenderer($hierarchy, $config['defaultRenderer']);
       
   219         }
       
   220     }
       
   221 
       
   222     private function configureRenderer(LoggerHierarchy $hierarchy, $config) {
       
   223         if (empty($config['renderingClass'])) {
       
   224             $this->warn("Rendering class not specified. Skipping renderer definition.");
       
   225             return;
       
   226         }
       
   227 
       
   228         if (empty($config['renderedClass'])) {
       
   229             $this->warn("Rendered class not specified. Skipping renderer definition.");
       
   230             return;
       
   231         }
       
   232 
       
   233         // Error handling performed by RendererMap
       
   234         $hierarchy->getRendererMap()->addRenderer($config['renderedClass'], $config['renderingClass']);
       
   235     }
       
   236 
       
   237     private function configureDefaultRenderer(LoggerHierarchy $hierarchy, $class) {
       
   238         if (empty($class)) {
       
   239             $this->warn("Rendering class not specified. Skipping default renderer definition.");
       
   240             return;
       
   241         }
       
   242 
       
   243         // Error handling performed by RendererMap
       
   244         $hierarchy->getRendererMap()->setDefaultRenderer($class);
       
   245     }
       
   246 
       
   247     /**
       
   248      * Configures an appender based on given config and saves it to
       
   249      * {@link $appenders} array so it can be later linked to loggers.
       
   250      * @param string $name Appender name.
       
   251      * @param array $config Appender configuration options.
       
   252      */
       
   253     private function configureAppender($name, $config) {
       
   254 
       
   255         // TODO: add this check to other places where it might be useful
       
   256         if (!is_array($config)) {
       
   257             $type = gettype($config);
       
   258             $this->warn("Invalid configuration provided for appender [$name]. Expected an array, found <$type>. Skipping appender definition.");
       
   259             return;
       
   260         }
       
   261 
       
   262         // Parse appender class
       
   263         $class = $config['class'];
       
   264         if (empty($class)) {
       
   265             $this->warn("No class given for appender [$name]. Skipping appender definition.");
       
   266             return;
       
   267         }
       
   268         if (!class_exists($class)) {
       
   269             $this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition.");
       
   270             return;
       
   271         }
       
   272 
       
   273         // Instantiate the appender
       
   274         $appender = new $class($name);
       
   275         if (!($appender instanceof LoggerAppender)) {
       
   276             $this->warn("Invalid class [$class] given for appender [$name]. Not a valid LoggerAppender class. Skipping appender definition.");
       
   277             return;
       
   278         }
       
   279 
       
   280         // Parse the appender threshold
       
   281         if (isset($config['threshold'])) {
       
   282             $threshold = LoggerLevel::toLevel($config['threshold']);
       
   283             if ($threshold instanceof LoggerLevel) {
       
   284                 $appender->setThreshold($threshold);
       
   285             } else {
       
   286                 $this->warn("Invalid threshold value [{$config['threshold']}] specified for appender [$name]. Ignoring threshold definition.");
       
   287             }
       
   288         }
       
   289 
       
   290         // Parse the appender layout
       
   291         if ($appender->requiresLayout() && isset($config['layout'])) {
       
   292             $this->createAppenderLayout($appender, $config['layout']);
       
   293         }
       
   294 
       
   295         // Parse filters
       
   296         if (isset($config['filters']) && is_array($config['filters'])) {
       
   297             foreach ($config['filters'] as $filterConfig) {
       
   298                 $this->createAppenderFilter($appender, $filterConfig);
       
   299             }
       
   300         }
       
   301 
       
   302         // Set options if any
       
   303         if (isset($config['params'])) {
       
   304             $this->setOptions($appender, $config['params']);
       
   305         }
       
   306 
       
   307         // Activate and save for later linking to loggers
       
   308         $appender->activateOptions();
       
   309         $this->appenders[$name] = $appender;
       
   310     }
       
   311 
       
   312     /**
       
   313      * Parses layout config, creates the layout and links it to the appender.
       
   314      * @param LoggerAppender $appender
       
   315      * @param array $config Layout configuration.
       
   316      */
       
   317     private function createAppenderLayout(LoggerAppender $appender, $config) {
       
   318         $name = $appender->getName();
       
   319         $class = $config['class'];
       
   320         if (empty($class)) {
       
   321             $this->warn("Layout class not specified for appender [$name]. Reverting to default layout.");
       
   322             return;
       
   323         }
       
   324         if (!class_exists($class)) {
       
   325             $this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout.");
       
   326             return;
       
   327         }
       
   328 
       
   329         $layout = new $class();
       
   330         if (!($layout instanceof LoggerLayout)) {
       
   331             $this->warn("Invalid layout class [$class] sepcified for appender [$name]. Reverting to default layout.");
       
   332             return;
       
   333         }
       
   334 
       
   335         if (isset($config['params'])) {
       
   336             $this->setOptions($layout, $config['params']);
       
   337         }
       
   338 
       
   339         $layout->activateOptions();
       
   340         $appender->setLayout($layout);
       
   341     }
       
   342 
       
   343     /**
       
   344      * Parses filter config, creates the filter and adds it to the appender's
       
   345      * filter chain.
       
   346      * @param LoggerAppender $appender
       
   347      * @param array $config Filter configuration.
       
   348      */
       
   349     private function createAppenderFilter(LoggerAppender $appender, $config) {
       
   350         $name = $appender->getName();
       
   351         $class = $config['class'];
       
   352         if (!class_exists($class)) {
       
   353             $this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition.");
       
   354             return;
       
   355         }
       
   356 
       
   357         $filter = new $class();
       
   358         if (!($filter instanceof LoggerFilter)) {
       
   359             $this->warn("Invalid filter class [$class] sepcified on appender [$name]. Skipping filter definition.");
       
   360             return;
       
   361         }
       
   362 
       
   363         if (isset($config['params'])) {
       
   364             $this->setOptions($filter, $config['params']);
       
   365         }
       
   366 
       
   367         $filter->activateOptions();
       
   368         $appender->addFilter($filter);
       
   369     }
       
   370 
       
   371     /**
       
   372      * Configures the root logger
       
   373      * @see configureLogger()
       
   374      */
       
   375     private function configureRootLogger(LoggerHierarchy $hierarchy, $config) {
       
   376         $logger = $hierarchy->getRootLogger();
       
   377         $this->configureLogger($logger, $config);
       
   378     }
       
   379 
       
   380     /**
       
   381      * Configures a logger which is not root.
       
   382      * @see configureLogger()
       
   383      */
       
   384     private function configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) {
       
   385         // Get logger from hierarchy (this creates it if it doesn't already exist)
       
   386         $logger = $hierarchy->getLogger($name);
       
   387         $this->configureLogger($logger, $config);
       
   388     }
       
   389 
       
   390     /**
       
   391      * Configures a logger.
       
   392      *
       
   393      * @param Logger $logger The logger to configure
       
   394      * @param array $config Logger configuration options.
       
   395      */
       
   396     private function configureLogger(Logger $logger, $config) {
       
   397         $loggerName = $logger->getName();
       
   398 
       
   399         // Set logger level
       
   400         if (isset($config['level'])) {
       
   401             $level = LoggerLevel::toLevel($config['level']);
       
   402             if (isset($level)) {
       
   403                 $logger->setLevel($level);
       
   404             } else {
       
   405                 $this->warn("Invalid level value [{$config['level']}] specified for logger [$loggerName]. Ignoring level definition.");
       
   406             }
       
   407         }
       
   408 
       
   409         // Link appenders to logger
       
   410         if (isset($config['appenders'])) {
       
   411             foreach ($config['appenders'] as $appenderName) {
       
   412                 if (isset($this->appenders[$appenderName])) {
       
   413                     $logger->addAppender($this->appenders[$appenderName]);
       
   414                 } else {
       
   415                     $this->warn("Nonexistnant appender [$appenderName] linked to logger [$loggerName].");
       
   416                 }
       
   417             }
       
   418         }
       
   419 
       
   420         // Set logger additivity
       
   421         if (isset($config['additivity'])) {
       
   422             try {
       
   423                 $additivity = LoggerOptionConverter::toBooleanEx($config['additivity'], null);
       
   424                 $logger->setAdditivity($additivity);
       
   425             } catch (Exception $ex) {
       
   426                 $this->warn("Invalid additivity value [{$config['additivity']}] specified for logger [$loggerName]. Ignoring additivity setting.");
       
   427             }
       
   428         }
       
   429     }
       
   430 
       
   431     /**
       
   432      * Helper method which applies given options to an object which has setters
       
   433      * for these options (such as appenders, layouts, etc.).
       
   434      *
       
   435      * For example, if options are:
       
   436      * <code>
       
   437      * array(
       
   438      *    'file' => '/tmp/myfile.log',
       
   439      *    'append' => true
       
   440      * )
       
   441      * </code>
       
   442      *
       
   443      * This method will call:
       
   444      * <code>
       
   445      * $object->setFile('/tmp/myfile.log')
       
   446      * $object->setAppend(true)
       
   447      * </code>
       
   448      *
       
   449      * If required setters do not exist, it will produce a warning.
       
   450      *
       
   451      * @param mixed $object The object to configure.
       
   452      * @param unknown_type $options
       
   453      */
       
   454     private function setOptions($object, $options) {
       
   455         foreach ($options as $name => $value) {
       
   456             $setter = "set$name";
       
   457             if (method_exists($object, $setter)) {
       
   458                 $object->$setter($value);
       
   459             } else {
       
   460                 $class = get_class($object);
       
   461                 $this->warn("Nonexistant option [$name] specified on [$class]. Skipping.");
       
   462             }
       
   463         }
       
   464     }
       
   465 
       
   466     /** Helper method to simplify error reporting. */
       
   467     private function warn($message) {
       
   468         trigger_error("log4php: $message", E_USER_WARNING);
       
   469     }
       
   470 }