3 * log4php is a PHP port of the log4j java logging package.
5 * <p>This framework is based on log4j (see {@link http://jakarta.apache.org/log4j log4j} for details).</p>
6 * <p>Design, strategies and part of the methods documentation are developed by log4j team
7 * (Ceki Gülcü as log4j project founder and
8 * {@link http://jakarta.apache.org/log4j/docs/contributors.html contributors}).</p>
10 * <p>PHP port, extensions and modifications by VxR. All rights reserved.<br>
11 * For more information, please see {@link http://www.vxr.it/log4php/}.</p>
13 * <p>This software is published under the terms of the LGPL License
14 * a copy of which has been included with this distribution in the LICENSE file.</p>
22 if (!defined('LOG4PHP_DIR')) define('LOG4PHP_DIR', dirname(__FILE__));
24 require_once(LOG4PHP_DIR . '/config/LoggerPropertySetter.php');
25 require_once(LOG4PHP_DIR . '/helpers/LoggerOptionConverter.php');
26 require_once(LOG4PHP_DIR . '/or/LoggerObjectRenderer.php');
27 require_once(LOG4PHP_DIR . '/or/LoggerRendererMap.php');
28 require_once(LOG4PHP_DIR . '/spi/LoggerConfigurator.php');
29 require_once(LOG4PHP_DIR . '/spi/LoggerFilter.php');
30 require_once(LOG4PHP_DIR . '/LoggerAppender.php');
31 require_once(LOG4PHP_DIR . '/LoggerDefaultCategoryFactory.php');
32 require_once(LOG4PHP_DIR . '/LoggerLayout.php');
33 require_once(LOG4PHP_DIR . '/LoggerLevel.php');
34 require_once(LOG4PHP_DIR . '/LoggerManager.php');
36 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_CATEGORY_PREFIX', "log4php.category.");
37 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_PREFIX', "log4php.logger.");
38 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_FACTORY_PREFIX', "log4php.factory");
39 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ADDITIVITY_PREFIX', "log4php.additivity.");
40 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_CATEGORY_PREFIX', "log4php.rootCategory");
41 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_LOGGER_PREFIX', "log4php.rootLogger");
42 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_APPENDER_PREFIX', "log4php.appender.");
43 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_RENDERER_PREFIX', "log4php.renderer.");
44 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_THRESHOLD_PREFIX', "log4php.threshold");
47 * Key for specifying the {@link LoggerFactory}.
49 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_FACTORY_KEY', "log4php.loggerFactory");
50 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_DEBUG_KEY', "log4php.debug");
51 define('LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_INTERNAL_ROOT_NAME', "root");
56 * Allows the configuration of log4php from an external file.
58 * See {@link doConfigure()} for the expected format.
60 * <p>It is sometimes useful to see how log4php is reading configuration
61 * files. You can enable log4php internal logging by defining the
62 * <b>log4php.debug</b> variable.</p>
64 * <p>The <i>LoggerPropertyConfigurator</i> does not handle the
65 * advanced configuration features supported by the {@link LoggerDOMConfigurator}
66 * such as support for {@link LoggerFilter},
67 custom {@link LoggerErrorHandlers}, nested appenders such as the
68 {@link Logger AsyncAppender},
71 * <p>All option <i>values</i> admit variable substitution. The
72 * syntax of variable substitution is similar to that of Unix
73 * shells. The string between an opening <b>"${"</b> and
74 * closing <b>"}"</b> is interpreted as a key. The value of
75 * the substituted variable can be defined as a system property or in
76 * the configuration file itself. The value of the key is first
77 * searched in the defined constants, in the enviroments variables
78 * and if not found there, it is
79 * then searched in the configuration file being parsed. The
80 * corresponding value replaces the ${variableName} sequence.</p>
81 * <p>For example, if <b>$_ENV['home']</b> env var is set to
82 * <b>/home/xyz</b>, then every occurrence of the sequence
83 * <b>${home}</b> will be interpreted as
84 * <b>/home/xyz</b>. See {@link LoggerOptionConverter::getSystemProperty()}
87 * @author VxR <vxr@vxr.it>
88 * @version $Revision: 2 $
92 class LoggerPropertyConfigurator extends LoggerConfigurator {
97 var $loggerFactory = null;
102 function LoggerPropertyConfigurator()
104 $this->loggerFactory = new LoggerDefaultCategoryFactory();
108 * Configure the default repository using the resource pointed by <b>url</b>.
109 * <b>Url</b> is any valid resurce as defined in {@link PHP_MANUAL#file} function.
110 * Note that the resource will be search with <i>use_include_path</i> parameter
114 * @return boolean configuration result
117 //static function configure($url = '') //
\8fC
\90³(2006/12/11)
118 function configure($url = '')
121 $configurator = new LoggerPropertyConfigurator();
122 $repository =& LoggerManager::getLoggerRepository();
123 return $configurator->doConfigure($url, $repository);
125 $configurator = new LoggerPropertyConfigurator();//
\8fC
\90³(2006/12/11)
126 $repository = LoggerManager::getLoggerRepository();
127 return $configurator->doConfigure($url, $repository);
131 * Read configuration from a file.
133 * <p>The function {@link PHP_MANUAL#parse_ini_file} is used to read the
136 * <b>The existing configuration is not cleared nor reset.</b>
137 * If you require a different behavior, then call
138 * {@link LoggerManager::resetConfiguration()}
139 * method before calling {@link doConfigure()}.
141 * <p>The configuration file consists of statements in the format
142 * <b>key=value</b>. The syntax of different configuration
143 * elements are discussed below.
145 * <p><b>Repository-wide threshold</b></p>
147 * <p>The repository-wide threshold filters logging requests by level
148 * regardless of logger. The syntax is:
151 * log4php.threshold=[level]
154 * <p>The level value can consist of the string values OFF, FATAL,
155 * ERROR, WARN, INFO, DEBUG, ALL or a <i>custom level</i> value. A
156 * custom level value can be specified in the form
157 * <samp>level#classname</samp>. By default the repository-wide threshold is set
158 * to the lowest possible value, namely the level <b>ALL</b>.
162 * <p><b>Appender configuration</b></p>
164 * <p>Appender configuration syntax is:</p>
166 * ; For appender named <i>appenderName</i>, set its class.
167 * ; Note: The appender name can contain dots.
168 * log4php.appender.appenderName=name_of_appender_class
170 * ; Set appender specific options.
172 * log4php.appender.appenderName.option1=value1
173 * log4php.appender.appenderName.optionN=valueN
176 * For each named appender you can configure its {@link LoggerLayout}. The
177 * syntax for configuring an appender's layout is:
179 * log4php.appender.appenderName.layout=name_of_layout_class
180 * log4php.appender.appenderName.layout.option1=value1
182 * log4php.appender.appenderName.layout.optionN=valueN
185 * <p><b>Configuring loggers</b></p>
187 * <p>The syntax for configuring the root logger is:
189 * log4php.rootLogger=[level], appenderName, appenderName, ...
192 * <p>This syntax means that an optional <i>level</i> can be
193 * supplied followed by appender names separated by commas.
195 * <p>The level value can consist of the string values OFF, FATAL,
196 * ERROR, WARN, INFO, DEBUG, ALL or a <i>custom level</i> value. A
197 * custom level value can be specified in the form</p>
199 * <pre>level#classname</pre>
201 * <p>If a level value is specified, then the root level is set
202 * to the corresponding level. If no level value is specified,
203 * then the root level remains untouched.
205 * <p>The root logger can be assigned multiple appenders.
207 * <p>Each <i>appenderName</i> (separated by commas) will be added to
208 * the root logger. The named appender is defined using the
209 * appender syntax defined above.
211 * <p>For non-root categories the syntax is almost the same:
213 * log4php.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
216 * <p>The meaning of the optional level value is discussed above
217 * in relation to the root logger. In addition however, the value
218 * INHERITED can be specified meaning that the named logger should
219 * inherit its level from the logger hierarchy.</p>
221 * <p>If no level value is supplied, then the level of the
222 * named logger remains untouched.</p>
224 * <p>By default categories inherit their level from the
225 * hierarchy. However, if you set the level of a logger and later
226 * decide that that logger should inherit its level, then you should
227 * specify INHERITED as the value for the level value. NULL is a
228 * synonym for INHERITED.</p>
230 * <p>Similar to the root logger syntax, each <i>appenderName</i>
231 * (separated by commas) will be attached to the named logger.</p>
233 * <p>See the <i>appender additivity rule</i> in the user manual for
234 * the meaning of the <b>additivity</b> flag.
236 * <p><b>ObjectRenderers</b></p>
238 * <p>You can customize the way message objects of a given type are
239 * converted to String before being logged. This is done by
240 * specifying a {@link LoggerObjectRenderer}
241 * for the object type would like to customize.</p>
246 * log4php.renderer.name_of_rendered_class=name_of_rendering.class
251 * log4php.renderer.myFruit=myFruitRenderer
254 * <p><b>Logger Factories</b></p>
256 * The usage of custom logger factories is discouraged and no longer
259 * <p><b>Example</b></p>
261 * <p>An example configuration is given below. Other configuration
262 * file examples are given in the <b>tests</b> folder.
265 * ; Set options for appender named "A1".
266 * ; Appender "A1" will be a SyslogAppender
267 * log4php.appender.A1=SyslogAppender
269 * ; The syslog daemon resides on www.abc.net
270 * log4php.appender.A1.SyslogHost=www.abc.net
272 * ; A1's layout is a LoggerPatternLayout, using the conversion pattern
273 * ; <b>%r %-5p %c{2} %M.%L %x - %m%n</b>. Thus, the log output will
274 * ; include the relative time since the start of the application in
275 * ; milliseconds, followed by the level of the log request,
276 * ; followed by the two rightmost components of the logger name,
277 * ; followed by the callers method name, followed by the line number,
278 * ; the nested disgnostic context and finally the message itself.
279 * ; Refer to the documentation of LoggerPatternLayout} for further information
280 * ; on the syntax of the ConversionPattern key.
281 * log4php.appender.A1.layout=LoggerPatternLayout
282 * log4php.appender.A1.layout.ConversionPattern="%-4r %-5p %c{2} %M.%L %x - %m%n"
284 * ; Set options for appender named "A2"
285 * ; A2 should be a LoggerAppenderRollingFile, with maximum file size of 10 MB
286 * ; using at most one backup file. A2's layout is TTCC, using the
287 * ; ISO8061 date format with context printing enabled.
288 * log4php.appender.A2=LoggerAppenderRollingFile
289 * log4php.appender.A2.MaxFileSize=10MB
290 * log4php.appender.A2.MaxBackupIndex=1
291 * log4php.appender.A2.layout=LoggerLayoutTTCC
292 * log4php.appender.A2.layout.ContextPrinting="true"
293 * log4php.appender.A2.layout.DateFormat="%c"
295 * ; Root logger set to DEBUG using the A2 appender defined above.
296 * log4php.rootLogger=DEBUG, A2
298 * ; Logger definitions:
299 * ; The SECURITY logger inherits is level from root. However, it's output
300 * ; will go to A1 appender defined above. It's additivity is non-cumulative.
301 * log4php.logger.SECURITY=INHERIT, A1
302 * log4php.additivity.SECURITY=false
304 * ; Only warnings or above will be logged for the logger "SECURITY.access".
305 * ; Output will go to A1.
306 * log4php.logger.SECURITY.access=WARN
309 * ; The logger "class.of.the.day" inherits its level from the
310 * ; logger hierarchy. Output will go to the appender's of the root
311 * ; logger, A2 in this case.
312 * log4php.logger.class.of.the.day=INHERIT
315 * <p>Refer to the <b>setOption</b> method in each Appender and
316 * Layout for class specific options.</p>
318 * <p>Use the <b>";"</b> character at the
319 * beginning of a line for comments.</p>
321 * @param string $url The name of the configuration file where the
322 * configuration information is stored.
323 * @param LoggerHierarchy &$repository the repository to apply the configuration
325 function doConfigure($url, &$repository)
327 $properties = @parse_ini_file($url);
328 if ($properties === false) {
329 LoggerLog::warn("LoggerPropertyConfigurator::doConfigure() cannot load '$url' configuration.");
332 return $this->doConfigureProperties($properties, $repository);
337 * Read configuration options from <b>properties</b>.
339 * @see doConfigure().
340 * @param array $properties
341 * @param LoggerHierarchy &$hierarchy
343 function doConfigureProperties($properties, &$hierarchy)
345 $value = @$properties[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_DEBUG_KEY];
347 if (!empty($value)) {
348 LoggerLog::internalDebugging(LoggerOptionConverter::toBoolean($value, LoggerLog::internalDebugging()));
351 $thresholdStr = @$properties[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_THRESHOLD_PREFIX];
352 $hierarchy->setThreshold(LoggerOptionConverter::toLevel($thresholdStr, LoggerLevel::getLevelAll()));
354 $this->configureRootCategory($properties, $hierarchy);
355 $this->configureLoggerFactory($properties);
356 $this->parseCatsAndRenderers($properties, $hierarchy);
358 LoggerLog::debug("LoggerPropertyConfigurator::doConfigureProperties() Finished configuring.");
363 // --------------------------------------------------------------------------
365 // --------------------------------------------------------------------------
368 * Check the provided <b>Properties</b> object for a
369 * {@link LoggerFactory} entry specified by
370 * {@link LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_FACTORY_KEY}.
372 * If such an entry exists, an attempt is made to create an instance using
373 * the default constructor.
374 * This instance is used for subsequent Category creations
375 * within this configurator.
377 * @see parseCatsAndRenderers()
378 * @param array $props array of properties
380 function configureLoggerFactory($props)
382 $factoryFqcn = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_FACTORY_KEY];
383 if(!empty($factoryFqcn)) {
384 $factoryClassName = basename($factoryFqcn);
386 "LoggerPropertyConfigurator::configureLoggerFactory() Trying to load factory [" .
391 if (!class_exists($factoryClassName))
392 @include_once("{$factoryFqcn}.php");
393 if (class_exists($factoryClassName)) {
394 $loggerFactory = new $factoryClassName();
397 "LoggerPropertyConfigurator::configureLoggerFactory() Unable to load factory [" .
401 $loggerFactory = $this->loggerFactory;
405 "LoggerPropertyConfigurator::configureLoggerFactory() ".
406 "Setting properties for category factory [" . get_class($loggerFactory) . "]."
409 LoggerPropertySetter::setPropertiesByObject($loggerFactory, $props, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_FACTORY_PREFIX . ".");
414 * @param array $props array of properties
415 * @param LoggerHierarchy &$hierarchy
417 function configureRootCategory($props, &$hierarchy)
419 $effectivePrefix = LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_LOGGER_PREFIX;
420 $value = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_LOGGER_PREFIX];
423 $value = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_CATEGORY_PREFIX];
424 $effectivePrefix = LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_CATEGORY_PREFIX;
429 "LoggerPropertyConfigurator::configureRootCategory() ".
430 "Could not find root logger information. Is this OK?"
433 $root =& $hierarchy->getRootLogger();
434 // synchronized(root) {
435 $this->parseCategory(
439 LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_INTERNAL_ROOT_NAME,
447 * Parse non-root elements, such non-root categories and renderers.
449 * @param array $props array of properties
450 * @param LoggerHierarchy &$hierarchy
452 function parseCatsAndRenderers($props, &$hierarchy)
454 while(list($key,$value) = each($props)) {
455 if( strpos($key, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_CATEGORY_PREFIX) === 0 or
456 strpos($key, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_PREFIX) === 0) {
457 if(strpos($key, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_CATEGORY_PREFIX) === 0) {
458 $loggerName = substr($key, strlen(LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_CATEGORY_PREFIX));
459 } elseif (strpos($key, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_PREFIX) === 0) {
460 $loggerName = substr($key, strlen(LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_PREFIX));
462 $logger =& $hierarchy->getLogger($loggerName, $this->loggerFactory);
463 // synchronized(logger) {
464 $this->parseCategory($props, $logger, $key, $loggerName, $value);
465 $this->parseAdditivityForLogger($props, $logger, $loggerName);
467 } elseif (strpos($key, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_RENDERER_PREFIX) === 0) {
468 $renderedClass = substr($key, strlen(LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_RENDERER_PREFIX));
469 $renderingClass = $value;
470 if (method_exists($hierarchy, 'addrenderer')) {
471 LoggerRendererMap::addRenderer($hierarchy, $renderedClass, $renderingClass);
478 * Parse the additivity option for a non-root category.
480 * @param array $props array of properties
481 * @param Logger &$cat
482 * @param string $loggerName
484 function parseAdditivityForLogger($props, &$cat, $loggerName)
486 $value = LoggerOptionConverter::findAndSubst(
487 LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ADDITIVITY_PREFIX . $loggerName,
491 "LoggerPropertyConfigurator::parseAdditivityForLogger() ".
492 "Handling " . LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ADDITIVITY_PREFIX . $loggerName . "=[{$value}]"
494 // touch additivity only if necessary
496 $additivity = LoggerOptionConverter::toBoolean($value, true);
498 "LoggerPropertyConfigurator::parseAdditivityForLogger() ".
499 "Setting additivity for [{$loggerName}] to [{$additivity}]"
501 $cat->setAdditivity($additivity);
506 * This method must work for the root category as well.
508 * @param array $props array of properties
509 * @param Logger &$logger
510 * @param string $optionKey
511 * @param string $loggerName
512 * @param string $value
515 //function &parseCategory($props, &$logger, $optionKey, $loggerName, $value)
516 function parseCategory($props, &$logger, $optionKey, $loggerName, $value) //
\8fC
\90³(2006/12/12)
519 "LoggerPropertyConfigurator::parseCategory() ".
520 "Parsing for [{$loggerName}] with value=[{$value}]."
523 // We must skip over ',' but not white space
524 $st = explode(',', $value);
526 // If value is not in the form ", appender.." or "", then we should set
527 // the level of the loggeregory.
529 if(!(@$value{0} == ',' || empty($value))) {
530 // just to be on the safe side...
534 $levelStr = current($st);
536 "LoggerPropertyConfigurator::parseCategory() ".
537 "Level token is [$levelStr]."
540 // If the level value is inherited, set category level value to
541 // null. We also check that the user has not specified inherited for the
543 if('INHERITED' == strtoupper($levelStr) || 'NULL' == strtoupper($levelStr)) {
544 if ($loggerName == LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_INTERNAL_ROOT_NAME) {
546 "LoggerPropertyConfigurator::parseCategory() ".
547 "The root logger cannot be set to null."
550 $logger->setLevel(null);
553 $logger->setLevel(LoggerOptionConverter::toLevel($levelStr, LoggerLevel::getLevelDebug()));
557 // Begin by removing all existing appenders.
558 $logger->removeAllAppenders();
559 while($appenderName = next($st)) {
560 $appenderName = trim($appenderName);
561 if(empty($appenderName))
564 "LoggerPropertyConfigurator::parseCategory() ".
565 "Parsing appender named [{$appenderName}]."
567 //$appender =& $this->parseAppender($props, $appenderName);
568 $appender = $this->parseAppender($props, $appenderName); //
\8fC
\90³(2006/12/12)
569 if($appender !== null) {
570 $logger->addAppender($appender);
576 * @param array $props array of properties
577 * @param string $appenderName
578 * @return LoggerAppender
580 //function &parseAppender($props, $appenderName)
581 function parseAppender($props, $appenderName) //
\8fC
\90³(2006/12/12)
583 //$appender =& LoggerAppender::singleton($appenderName);
584 $appender = LoggerAppender::singleton($appenderName); //
\8fC
\90³(2006/12/12)
585 if($appender !== null) {
587 "LoggerPropertyConfigurator::parseAppender() ".
588 "Appender [{$appenderName}] was already parsed."
592 // Appender was not previously initialized.
593 $prefix = LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_APPENDER_PREFIX . $appenderName;
594 $layoutPrefix = $prefix . ".layout";
595 $appenderClass = @$props[$prefix];
596 if (!empty($appenderClass)) {
597 //$appender =& LoggerAppender::singleton($appenderName, $appenderClass);
598 $appender = LoggerAppender::singleton($appenderName, $appenderClass); //
\8fC
\90³(2006/12/12)
599 if($appender === null) {
601 "LoggerPropertyConfigurator::parseAppender() ".
602 "Could not instantiate appender named [$appenderName]."
608 "LoggerPropertyConfigurator::parseAppender() ".
609 "Could not instantiate appender named [$appenderName] with null className."
614 $appender->setName($appenderName);
615 if( $appender->requiresLayout() ) {
617 "LoggerPropertyConfigurator::parseAppender() ".
618 "Parsing layout section for [$appenderName]."
620 $layoutClass = @$props[$layoutPrefix];
621 $layoutClass = LoggerOptionConverter::substVars($layoutClass, $props);
622 if (empty($layoutClass)) {
624 "LoggerPropertyConfigurator::parseAppender() ".
625 "layout class is empty in '$layoutPrefix'. Using Simple layout"
627 $layout = LoggerLayout::factory('LoggerLayoutSimple');
629 $layout = LoggerLayout::factory($layoutClass);
631 if($layout === null) {
633 "LoggerPropertyConfigurator::parseAppender() ".
634 "cannot create layout '$layoutClass'. Using Simple layout"
636 $layout = LoggerLayout::factory('LoggerLayoutSimple');
641 "LoggerPropertyConfigurator::parseAppender() ".
642 "Parsing layout options for [$appenderName]."
644 LoggerPropertySetter::setPropertiesByObject($layout, $props, $layoutPrefix . ".");
646 "LoggerPropertyConfigurator::parseAppender() ".
647 "End Parsing layout options for [$appenderName]."
649 $appender->setLayout($layout);
652 LoggerPropertySetter::setPropertiesByObject($appender, $props, $prefix . ".");
654 "LoggerPropertyConfigurator::parseAppender() ".
655 "Parsed [{$appenderName}] options."