'LoggerConfigurationAdapterXML', self::FORMAT_INI => 'LoggerConfigurationAdapterINI', self::FORMAT_PHP => 'LoggerConfigurationAdapterPHP', ); /** Default configuration; used if no configuration file is provided. */ private static $defaultConfiguration = array( 'threshold' => 'ALL', 'rootLogger' => array( 'level' => 'DEBUG', 'appenders' => array('default'), ), 'appenders' => array( 'default' => array( 'class' => 'LoggerAppenderEcho', 'layout' => array( 'class' => 'LoggerLayoutTTCC', ), ), ), ); /** Holds the appenders before they are linked to loggers. */ private $appenders = array(); /** * Configures log4php based on the given configuration. The input can * either be a path to the config file, or a PHP array holding the * configuration. * * If no configuration is given, or if the given configuration cannot be * parsed for whatever reason, a warning will be issued, and log4php * will use the default configuration contained in * {@link $defaultConfiguration}. * * @param LoggerHierarchy $hierarchy The hierarchy on which to perform * the configuration. * @param string|array $input Either path to the config file or the * configuration as an array. If not set, default configuration * will be used. */ public function configure(LoggerHierarchy $hierarchy, $input = null) { $config = $this->parse($input); $this->doConfigure($hierarchy, $config); } /** * Parses the given configuration and returns the parsed configuration * as a PHP array. Does not perform any configuration. * * If no configuration is given, or if the given configuration cannot be * parsed for whatever reason, a warning will be issued, and the default * configuration will be returned ({@link $defaultConfiguration}). * * @param string|array $input Either path to the config file or the * configuration as an array. If not set, default configuration * will be used. * @return array The parsed configuration. */ public function parse($input) { // No input - use default configuration if (!isset($input)) { $config = self::$defaultConfiguration; } // Array input - contains configuration within the array else if (is_array($input)) { $config = $input; } // String input - contains path to configuration file else if (is_string($input)) { try { $config = $this->parseFile($input); } catch (LoggerException $e) { $this->warn("Configuration failed. " . $e->getMessage() . " Using default configuration."); $config = self::$defaultConfiguration; } } // Anything else is an error else { $this->warn("Invalid configuration param given. Reverting to default configuration."); $config = self::$defaultConfiguration; } return $config; } /** * Returns the default log4php configuration. * @return array */ public static function getDefaultConfiguration() { return self::$defaultConfiguration; } /** * Loads the configuration file from the given URL, determines which * adapter to use, converts the configuration to a PHP array and * returns it. * * @param string $url Path to the config file. * @return The configuration from the config file, as a PHP array. * @throws LoggerException If the configuration file cannot be loaded, or * if the parsing fails. */ private function parseFile($url) { if (!file_exists($url)) { throw new LoggerException("File not found at [$url]."); } $type = $this->getConfigType($url); $adapterClass = $this->adapters[$type]; $adapter = new $adapterClass(); return $adapter->convert($url); } /** Determines configuration file type based on the file extension. */ private function getConfigType($url) { $info = pathinfo($url); $ext = strtolower($info['extension']); switch($ext) { case 'xml': return self::FORMAT_XML; case 'ini': case 'properties': return self::FORMAT_INI; case 'php': return self::FORMAT_PHP; default: throw new LoggerException("Unsupported configuration file extension: $ext"); } } /** * Constructs the logger hierarchy based on configuration. * * @param LoggerHierarchy $hierarchy * @param array $config */ private function doConfigure(LoggerHierarchy $hierarchy, $config) { if (isset($config['threshold'])) { $threshold = LoggerLevel::toLevel($config['threshold']); if (isset($threshold)) { $hierarchy->setThreshold($threshold); } else { $this->warn("Invalid threshold value [{$config['threshold']}] specified. Ignoring threshold definition."); } } // Configure appenders and add them to the appender pool if (isset($config['appenders']) && is_array($config['appenders'])) { foreach($config['appenders'] as $name => $appenderConfig) { $this->configureAppender($name, $appenderConfig); } } // Configure root logger if (isset($config['rootLogger'])) { $this->configureRootLogger($hierarchy, $config['rootLogger']); } // Configure loggers if (isset($config['loggers']) && is_array($config['loggers'])) { foreach($config['loggers'] as $loggerName => $loggerConfig) { $this->configureOtherLogger($hierarchy, $loggerName, $loggerConfig); } } // Configure renderers if (isset($config['renderers']) && is_array($config['renderers'])) { foreach($config['renderers'] as $rendererConfig) { $this->configureRenderer($hierarchy, $rendererConfig); } } } private function configureRenderer(LoggerHierarchy $hierarchy, $config) { if (!isset($config['renderingClass'])) { $this->warn("Rendering class not specified. Skipping renderers definition."); return; } $renderingClass = $config['renderingClass']; if (!class_exists($renderingClass)) { $this->warn("Nonexistant rendering class [$renderingClass] specified. Skipping renderers definition."); return; } $renderingClassInstance = new $renderingClass(); if (!$renderingClassInstance instanceof LoggerRendererObject) { $this->warn("Invalid class [$renderingClass] given. Not a valid LoggerRenderer class. Skipping renderers definition."); return; } if (!isset($config['renderedClass'])) { $this->warn("Rendered class not specified for rendering Class [$renderingClass]. Skipping renderers definition."); return; } $renderedClass = $config['renderedClass']; if (!class_exists($renderedClass)) { $this->warn("Nonexistant rendered class [$renderedClass] specified for renderer [$renderingClass]. Skipping renderers definition."); return; } $hierarchy->getRendererMap()->addRenderer($renderedClass, $renderingClassInstance); } /** * Configures an appender based on given config and saves it to * {@link $appenders} array so it can be later linked to loggers. * @param string $name Appender name. * @param array $config Appender configuration options. */ private function configureAppender($name, $config) { // TODO: add this check to other places where it might be useful if (!is_array($config)) { $type = gettype($config); $this->warn("Invalid configuration provided for appender [$name]. Expected an array, found <$type>. Skipping appender definition."); return; } // Parse appender class $class = $config['class']; if (empty($class)) { $this->warn("No class given for appender [$name]. Skipping appender definition."); return; } if (!class_exists($class)) { $this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition."); return; } // Instantiate the appender $appender = new $class($name); if (!($appender instanceof LoggerAppender)) { $this->warn("Invalid class [$class] given for appender [$name]. Not a valid LoggerAppender class. Skipping appender definition."); return; } // Parse the appender threshold if (isset($config['threshold'])) { $threshold = LoggerLevel::toLevel($config['threshold']); if ($threshold instanceof LoggerLevel) { $appender->setThreshold($threshold); } else { $this->warn("Invalid threshold value [{$config['threshold']}] specified for appender [$name]. Ignoring threshold definition."); } } // Parse the appender layout if ($appender->requiresLayout() && isset($config['layout'])) { $this->createAppenderLayout($appender, $config['layout']); } // Parse filters if (isset($config['filters']) && is_array($config['filters'])) { foreach($config['filters'] as $filterConfig) { $this->createAppenderFilter($appender, $filterConfig); } } // Set options if any if (isset($config['params'])) { $this->setOptions($appender, $config['params']); } // Activate and save for later linking to loggers $appender->activateOptions(); $this->appenders[$name] = $appender; } /** * Parses layout config, creates the layout and links it to the appender. * @param LoggerAppender $appender * @param array $config Layout configuration. */ private function createAppenderLayout(LoggerAppender $appender, $config) { $name = $appender->getName(); $class = $config['class']; if (empty($class)) { $this->warn("Layout class not specified for appender [$name]. Reverting to default layout."); return; } if (!class_exists($class)) { $this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout."); return; } $layout = new $class(); if (!($layout instanceof LoggerLayout)) { $this->warn("Invalid layout class [$class] sepcified for appender [$name]. Reverting to default layout."); return; } if (isset($config['params'])) { $this->setOptions($layout, $config['params']); } $layout->activateOptions(); $appender->setLayout($layout); } /** * Parses filter config, creates the filter and adds it to the appender's * filter chain. * @param LoggerAppender $appender * @param array $config Filter configuration. */ private function createAppenderFilter(LoggerAppender $appender, $config) { $name = $appender->getName(); $class = $config['class']; if (!class_exists($class)) { $this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition."); return; } $filter = new $class(); if (!($filter instanceof LoggerFilter)) { $this->warn("Invalid filter class [$class] sepcified on appender [$name]. Skipping filter definition."); return; } if (isset($config['params'])) { $this->setOptions($filter, $config['params']); } $filter->activateOptions(); $appender->addFilter($filter); } /** * Configures the root logger * @see configureLogger() */ private function configureRootLogger(LoggerHierarchy $hierarchy, $config) { $logger = $hierarchy->getRootLogger(); $this->configureLogger($logger, $config); } /** * Configures a logger which is not root. * @see configureLogger() */ private function configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) { // Get logger from hierarchy (this creates it if it doesn't already exist) $logger = $hierarchy->getLogger($name); $this->configureLogger($logger, $config); } /** * Configures a logger. * * @param Logger $logger The logger to configure * @param array $config Logger configuration options. */ private function configureLogger(Logger $logger, $config) { $loggerName = $logger->getName(); // Set logger level if (isset($config['level'])) { $level = LoggerLevel::toLevel($config['level']); if (isset($level)) { $logger->setLevel($level); } else { $default = $logger->getLevel(); $this->warn("Invalid level value [{$config['level']}] specified for logger [$loggerName]. Ignoring level definition."); } } // Link appenders to logger if (isset($config['appenders'])) { foreach($config['appenders'] as $appenderName) { if (isset($this->appenders[$appenderName])) { $logger->addAppender($this->appenders[$appenderName]); } else { $this->warn("Nonexistnant appender [$appenderName] linked to logger [$loggerName]."); } } } // Set logger additivity if (isset($config['additivity'])) { $additivity = LoggerOptionConverter::toBoolean($config['additivity'], null); if (is_bool($additivity)) { $logger->setAdditivity($additivity); } else { $this->warn("Invalid additivity value [{$config['additivity']}] specified for logger [$loggerName]. Ignoring additivity setting."); } } } /** * Helper method which applies given options to an object which has setters * for these options (such as appenders, layouts, etc.). * * For example, if options are: * * array( * 'file' => '/tmp/myfile.log', * 'append' => true * ) * * * This method will call: * * $object->setFile('/tmp/myfile.log') * $object->setAppend(true) * * * If required setters do not exist, it will produce a warning. * * @param mixed $object The object to configure. * @param unknown_type $options */ private function setOptions($object, $options) { foreach($options as $name => $value) { $setter = "set$name"; if (method_exists($object, $setter)) { $object->$setter($value); } else { $class = get_class($object); $this->warn("Nonexistant option [$name] specified on [$class]. Skipping."); } } } /** Helper method to simplify error reporting. */ private function warn($message) { trigger_error("log4php: $message", E_USER_WARNING); } }