This is the most commonly used method of configuring log4php.
* *It is sometimes useful to see how log4php is reading configuration * files. You can enable log4php internal logging by defining the * log4php.debug variable.
* *The LoggerConfiguratorIni does not handle the * advanced configuration features supported by the {@link LoggerConfiguratorXml} * such as support for {@link LoggerFilter}, * custom {@link LoggerErrorHandlers}, nested appenders such as the {@link Logger AsyncAppender}, * etc.
* *All option values admit variable substitution. The * syntax of variable substitution is similar to that of Unix * shells. The string between an opening "${" and * closing "}" is interpreted as a key. The value of * the substituted variable can be defined as a system property or in * the configuration file itself. The value of the key is first * searched in the defined constants, in the enviroments variables * and if not found there, it is * then searched in the configuration file being parsed. The * corresponding value replaces the ${variableName} sequence.
*For example, if $_ENV['home'] env var is set to * /home/xyz, then every occurrence of the sequence * ${home} will be interpreted as * /home/xyz. See {@link LoggerOptionConverter::getSystemProperty()} * for details.
* *An example how to use this appender:
* * {@example ../../examples/php/appender_dailyfile.php 19} * *And the corresponding ini file:
* * {@example ../../examples/resources/appender_dailyfile.properties 18} * * @version $Revision: 883104 $ * @package log4php * @subpackage configurators * @since 0.5 */ class LoggerConfiguratorIni implements LoggerConfigurator { const CATEGORY_PREFIX = "log4php.category."; const LOGGER_PREFIX = "log4php.logger."; const FACTORY_PREFIX = "log4php.factory"; const ADDITIVITY_PREFIX = "log4php.additivity."; const ROOT_CATEGORY_PREFIX = "log4php.rootCategory"; const ROOT_LOGGER_PREFIX = "log4php.rootLogger"; const APPENDER_PREFIX = "log4php.appender."; const RENDERER_PREFIX = "log4php.renderer."; const THRESHOLD_PREFIX = "log4php.threshold"; /** * Key for specifying the {@link LoggerFactory}. */ const LOGGER_FACTORY_KEY = "log4php.loggerFactory"; const LOGGER_DEBUG_KEY = "log4php.debug"; const INTERNAL_ROOT_NAME = "root"; /** * Constructor */ public function __construct() { } /** * Read configuration from a file. * *The function {@link PHP_MANUAL#parse_ini_file} is used to read the * file.
* * The existing configuration is not cleared nor reset. * If you require a different behavior, then call * {@link Logger::resetConfiguration()} * method before calling {@link doConfigure()}. * *The configuration file consists of statements in the format * key=value. The syntax of different configuration * elements are discussed below. * *
Repository-wide threshold
* *The repository-wide threshold filters logging requests by level * regardless of logger. The syntax is: * *
* log4php.threshold=[level] ** *
The level value can consist of the string values OFF, FATAL, * ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A * custom level value can be specified in the form * level#classname. By default the repository-wide threshold is set * to the lowest possible value, namely the level ALL. *
* * *Appender configuration
* *Appender configuration syntax is:
** ; For appender named appenderName, set its class. * ; Note: The appender name can contain dots. * log4php.appender.appenderName=name_of_appender_class * * ; Set appender specific options. * * log4php.appender.appenderName.option1=value1 * log4php.appender.appenderName.optionN=valueN ** * For each named appender you can configure its {@link LoggerLayout}. The * syntax for configuring an appender's layout is: *
* log4php.appender.appenderName.layout=name_of_layout_class * log4php.appender.appenderName.layout.option1=value1 * .... * log4php.appender.appenderName.layout.optionN=valueN ** *
Configuring loggers
* *The syntax for configuring the root logger is: *
* log4php.rootLogger=[level], appenderName, appenderName, ... ** *
This syntax means that an optional level can be * supplied followed by appender names separated by commas. * *
The level value can consist of the string values OFF, FATAL, * ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A * custom level value can be specified in the form
* *level#classname* *
If a level value is specified, then the root level is set * to the corresponding level. If no level value is specified, * then the root level remains untouched. * *
The root logger can be assigned multiple appenders. * *
Each appenderName (separated by commas) will be added to * the root logger. The named appender is defined using the * appender syntax defined above. * *
For non-root categories the syntax is almost the same: *
* log4php.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ... ** *
The meaning of the optional level value is discussed above * in relation to the root logger. In addition however, the value * INHERITED can be specified meaning that the named logger should * inherit its level from the logger hierarchy.
* *If no level value is supplied, then the level of the * named logger remains untouched.
* *By default categories inherit their level from the * hierarchy. However, if you set the level of a logger and later * decide that that logger should inherit its level, then you should * specify INHERITED as the value for the level value. NULL is a * synonym for INHERITED.
* *Similar to the root logger syntax, each appenderName * (separated by commas) will be attached to the named logger.
* *See the appender additivity rule in the user manual for * the meaning of the additivity flag. * *
ObjectRenderers
* *You can customize the way message objects of a given type are * converted to String before being logged. This is done by * specifying a {@link LoggerRendererObject} * for the object type would like to customize.
* *The syntax is: * *
* log4php.renderer.name_of_rendered_class=name_of_rendering.class ** * As in, *
* log4php.renderer.myFruit=myFruitRenderer ** *
Logger Factories
* * The usage of custom logger factories is discouraged and no longer * documented. * *Example
* *An example configuration is given below. Other configuration * file examples are given in the tests folder. * *
* ; Set options for appender named "A1". * ; Appender "A1" will be a LoggerAppenderSyslog * log4php.appender.A1=LoggerAppenderSyslog * * ; The syslog daemon resides on www.abc.net * log4php.appender.A1.ident=log4php-test * * ; A1's layout is a LoggerPatternLayout, using the conversion pattern * ; %r %-5p %c{2} %M.%L %x - %m%n. Thus, the log output will * ; include the relative time since the start of the application in * ; milliseconds, followed by the level of the log request, * ; followed by the two rightmost components of the logger name, * ; followed by the callers method name, followed by the line number, * ; the nested disgnostic context and finally the message itself. * ; Refer to the documentation of LoggerPatternLayout} for further information * ; on the syntax of the ConversionPattern key. * log4php.appender.A1.layout=LoggerPatternLayout * log4php.appender.A1.layout.ConversionPattern="%-4r %-5p %c{2} %M.%L %x - %m%n" * * ; Set options for appender named "A2" * ; A2 should be a LoggerAppenderRollingFile, with maximum file size of 10 MB * ; using at most one backup file. A2's layout is TTCC, using the * ; ISO8061 date format with context printing enabled. * log4php.appender.A2=LoggerAppenderRollingFile * log4php.appender.A2.MaxFileSize=10MB * log4php.appender.A2.MaxBackupIndex=1 * log4php.appender.A2.layout=LoggerLayoutTTCC * log4php.appender.A2.layout.ContextPrinting="true" * log4php.appender.A2.layout.DateFormat="%c" * * ; Root logger set to DEBUG using the A2 appender defined above. * log4php.rootLogger=DEBUG, A2 * * ; Logger definitions: * ; The SECURITY logger inherits is level from root. However, it's output * ; will go to A1 appender defined above. It's additivity is non-cumulative. * log4php.logger.SECURITY=INHERIT, A1 * log4php.additivity.SECURITY=false * * ; Only warnings or above will be logged for the logger "SECURITY.access". * ; Output will go to A1. * log4php.logger.SECURITY.access=WARN * * * ; The logger "class.of.the.day" inherits its level from the * ; logger hierarchy. Output will go to the appender's of the root * ; logger, A2 in this case. * log4php.logger.class.of.the.day=INHERIT ** *
Refer to the setOption method in each Appender and * Layout for class specific options.
* *Use the ";" character at the * beginning of a line for comments.
* * @param string $url The name of the configuration file where the * configuration information is stored. * @param LoggerHierarchy $repository the repository to apply the configuration */ public function configure(LoggerHierarchy $hierarchy, $url = '') { $properties = @parse_ini_file($url); if ($properties === false || count($properties) == 0) { $error = error_get_last(); throw new LoggerException("LoggerConfiguratorIni: ".$error['message']); } return $this->doConfigureProperties($properties, $hierarchy); } /** * Read configuration options from properties. * * @see doConfigure(). * @param array $properties * @param LoggerHierarchy $hierarchy */ private function doConfigureProperties($properties, LoggerHierarchy $hierarchy) { $thresholdStr = @$properties[self::THRESHOLD_PREFIX]; $hierarchy->setThreshold(LoggerOptionConverter::toLevel($thresholdStr, LoggerLevel::getLevelAll())); $this->configureRootCategory($properties, $hierarchy); $this->parseCatsAndRenderers($properties, $hierarchy); return true; } /** * @param array $props array of properties * @param LoggerHierarchy $hierarchy */ private function configureRootCategory($props, LoggerHierarchy $hierarchy) { $effectivePrefix = self::ROOT_LOGGER_PREFIX; $value = @$props[self::ROOT_LOGGER_PREFIX]; if(empty($value)) { $value = @$props[self::ROOT_CATEGORY_PREFIX]; $effectivePrefix = self::ROOT_CATEGORY_PREFIX; } if(empty($value)) { // TODO "Could not find root logger information. Is this OK?" } else { $root = $hierarchy->getRootLogger(); $this->parseCategory($props, $root, $effectivePrefix, self::INTERNAL_ROOT_NAME, $value); } } /** * Parse non-root elements, such non-root categories and renderers. * * @param array $props array of properties * @param LoggerHierarchy $hierarchy */ private function parseCatsAndRenderers($props, LoggerHierarchy $hierarchy) { while(list($key,$value) = each($props)) { if(strpos($key, self::CATEGORY_PREFIX) === 0 or strpos($key, self::LOGGER_PREFIX) === 0) { if(strpos($key, self::CATEGORY_PREFIX) === 0) { $loggerName = substr($key, strlen(self::CATEGORY_PREFIX)); } else if(strpos($key, self::LOGGER_PREFIX) === 0) { $loggerName = substr($key, strlen(self::LOGGER_PREFIX)); } $logger = $hierarchy->getLogger($loggerName); $this->parseCategory($props, $logger, $key, $loggerName, $value); $this->parseAdditivityForLogger($props, $logger, $loggerName); } else if(strpos($key, self::RENDERER_PREFIX) === 0) { $renderedClass = substr($key, strlen(self::RENDERER_PREFIX)); $renderingClass = $value; $hierarchy->getRendererMap()->addRenderer($renderedClass, $renderingClass); } } } /** * Parse the additivity option for a non-root category. * * @param array $props array of properties * @param Logger $cat * @param string $loggerName */ private function parseAdditivityForLogger($props, Logger $cat, $loggerName) { $value = LoggerOptionConverter::findAndSubst(self::ADDITIVITY_PREFIX . $loggerName, $props); // touch additivity only if necessary if(!empty($value)) { $additivity = LoggerOptionConverter::toBoolean($value, true); $cat->setAdditivity($additivity); } } /** * This method must work for the root category as well. * * @param array $props array of properties * @param Logger $logger * @param string $optionKey * @param string $loggerName * @param string $value * @return Logger */ private function parseCategory($props, Logger $logger, $optionKey, $loggerName, $value) { // We must skip over ',' but not white space $st = explode(',', $value); // If value is not in the form ", appender.." or "", then we should set // the level of the loggeregory. if(!(empty($value) || @$value[0] == ',')) { // just to be on the safe side... if(count($st) == 0) { return; } $levelStr = current($st); // If the level value is inherited, set category level value to // null. We also check that the user has not specified inherited for the // root category. if('INHERITED' == strtoupper($levelStr) || 'NULL' == strtoupper($levelStr)) { if($loggerName == self::INTERNAL_ROOT_NAME) { // TODO: throw exception? "The root logger cannot be set to null." } else { $logger->setLevel(null); } } else { $logger->setLevel(LoggerOptionConverter::toLevel($levelStr, LoggerLevel::getLevelDebug())); } } // TODO: removing should be done by the logger, if necessary and wanted // $logger->removeAllAppenders(); while($appenderName = next($st)) { $appenderName = trim($appenderName); if(empty($appenderName)) { continue; } $appender = $this->parseAppender($props, $appenderName); if($appender !== null) { $logger->addAppender($appender); } } } /** * @param array $props array of properties * @param string $appenderName * @return LoggerAppender */ private function parseAppender($props, $appenderName) { $appender = LoggerAppenderPool::getAppenderFromPool($appenderName); $prefix = self::APPENDER_PREFIX . $appenderName; if($appender === null) { // Appender was not previously initialized. $appenderClass = @$props[$prefix]; $appender = LoggerAppenderPool::getAppenderFromPool($appenderName, $appenderClass); if($appender === null) { return null; } } if($appender->requiresLayout() ) { $layoutPrefix = $prefix . ".layout"; $layoutClass = @$props[$layoutPrefix]; $layoutClass = LoggerOptionConverter::substVars($layoutClass, $props); if(empty($layoutClass)) { $layout = LoggerReflectionUtils::createObject('LoggerLayoutSimple'); } else { $layout = LoggerReflectionUtils::createObject($layoutClass); if($layout === null) { $layout = LoggerReflectionUtils::createObject('LoggerLayoutSimple'); } } LoggerReflectionUtils::setPropertiesByObject($layout, $props, $layoutPrefix . "."); $appender->setLayout($layout); } LoggerReflectionUtils::setPropertiesByObject($appender, $props, $prefix . "."); return $appender; } }