OSDN Git Service

初回コミット(v2.6.17.1)
[magic3/magic3.git] / include / lib / log4php / LoggerPropertyConfigurator.php
1 <?php
2 /**
3  * log4php is a PHP port of the log4j java logging package.
4  * 
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>
9  *
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>
12  *
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>
15  * 
16  * @package log4php
17  */
18
19 /**
20  * @ignore
21  */
22 if (!defined('LOG4PHP_DIR')) define('LOG4PHP_DIR', dirname(__FILE__));
23
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');
35
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");
45
46 /** 
47  * Key for specifying the {@link LoggerFactory}.  
48  */
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");
52
53
54
55 /**
56  * Allows the configuration of log4php from an external file.
57  * 
58  * See {@link doConfigure()} for the expected format.
59  * 
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>
63  *
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}, 
69  * etc.
70  * 
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>&quot;${&quot;</b> and
74  * closing <b>&quot;}&quot;</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()}
85  * for details.</p>
86  *
87  * @author VxR <vxr@vxr.it>
88  * @version $Revision: 2 $
89  * @package log4php
90  * @since 0.5 
91  */
92 class LoggerPropertyConfigurator extends LoggerConfigurator {
93
94     /**
95      * @var LoggerFactory
96      */
97     var $loggerFactory = null;
98     
99     /**
100      * Constructor
101      */
102     function LoggerPropertyConfigurator()
103     {
104         $this->loggerFactory = new LoggerDefaultCategoryFactory();
105     }
106     
107     /**
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 
111      * set to "1".
112      *
113      * @param string $url
114      * @return boolean configuration result
115      * @static
116      */
117     //static function configure($url = '')              // \8fC\90³(2006/12/11)
118         function configure($url = '')
119     {
120         /*
121         $configurator = new LoggerPropertyConfigurator();
122         $repository =& LoggerManager::getLoggerRepository();
123         return $configurator->doConfigure($url, $repository);
124         */
125         $configurator = new LoggerPropertyConfigurator();// \8fC\90³(2006/12/11)
126         $repository = LoggerManager::getLoggerRepository();
127         return $configurator->doConfigure($url, $repository);
128     }
129
130     /**
131      * Read configuration from a file.
132      *
133      * <p>The function {@link PHP_MANUAL#parse_ini_file} is used to read the
134      * file.</p>
135      *
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()}.
140      * 
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.
144      * 
145      * <p><b>Repository-wide threshold</b></p>
146      * 
147      * <p>The repository-wide threshold filters logging requests by level
148      * regardless of logger. The syntax is:
149      * 
150      * <pre>
151      * log4php.threshold=[level]
152      * </pre>
153      * 
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>.
159      * </p>
160      * 
161      * 
162      * <p><b>Appender configuration</b></p>
163      * 
164      * <p>Appender configuration syntax is:</p>
165      * <pre>
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
169      * 
170      * ; Set appender specific options.
171      * 
172      * log4php.appender.appenderName.option1=value1
173      * log4php.appender.appenderName.optionN=valueN
174      * </pre>
175      * 
176      * For each named appender you can configure its {@link LoggerLayout}. The
177      * syntax for configuring an appender's layout is:
178      * <pre>
179      * log4php.appender.appenderName.layout=name_of_layout_class
180      * log4php.appender.appenderName.layout.option1=value1
181      *  ....
182      * log4php.appender.appenderName.layout.optionN=valueN
183      * </pre>
184      * 
185      * <p><b>Configuring loggers</b></p>
186      * 
187      * <p>The syntax for configuring the root logger is:
188      * <pre>
189      * log4php.rootLogger=[level], appenderName, appenderName, ...
190      * </pre>
191      * 
192      * <p>This syntax means that an optional <i>level</i> can be
193      * supplied followed by appender names separated by commas.
194      * 
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>
198      *
199      * <pre>level#classname</pre>
200      * 
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.
204      * 
205      * <p>The root logger can be assigned multiple appenders.
206      * 
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.
210      * 
211      * <p>For non-root categories the syntax is almost the same:
212      * <pre>
213      * log4php.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
214      * </pre>
215      * 
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>
220      * 
221      * <p>If no level value is supplied, then the level of the
222      * named logger remains untouched.</p>
223      * 
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>
229      * 
230      * <p>Similar to the root logger syntax, each <i>appenderName</i>
231      * (separated by commas) will be attached to the named logger.</p>
232      * 
233      * <p>See the <i>appender additivity rule</i> in the user manual for 
234      * the meaning of the <b>additivity</b> flag.
235      * 
236      * <p><b>ObjectRenderers</b></p>
237      * 
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>
242      * 
243      * <p>The syntax is:
244      * 
245      * <pre>
246      * log4php.renderer.name_of_rendered_class=name_of_rendering.class
247      * </pre>
248      * 
249      * As in,
250      * <pre>
251      * log4php.renderer.myFruit=myFruitRenderer
252      * </pre>
253      * 
254      * <p><b>Logger Factories</b></p>
255      * 
256      * The usage of custom logger factories is discouraged and no longer
257      * documented.
258      * 
259      * <p><b>Example</b></p>
260      * 
261      * <p>An example configuration is given below. Other configuration
262      * file examples are given in the <b>tests</b> folder.
263      * 
264      * <pre>
265      * ; Set options for appender named "A1".
266      * ; Appender "A1" will be a SyslogAppender
267      * log4php.appender.A1=SyslogAppender
268      * 
269      * ; The syslog daemon resides on www.abc.net
270      * log4php.appender.A1.SyslogHost=www.abc.net
271      * 
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"
283      * 
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"
294      * 
295      * ; Root logger set to DEBUG using the A2 appender defined above.
296      * log4php.rootLogger=DEBUG, A2
297      * 
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
303      * 
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
307      * 
308      * 
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
313      * </pre>
314      * 
315      * <p>Refer to the <b>setOption</b> method in each Appender and
316      * Layout for class specific options.</p>
317      * 
318      * <p>Use the <b>&quot;;&quot;</b> character at the
319      * beginning of a line for comments.</p>
320      * 
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
324      */
325     function doConfigure($url, &$repository)
326     {
327         $properties = @parse_ini_file($url);
328         if ($properties === false) {
329             LoggerLog::warn("LoggerPropertyConfigurator::doConfigure() cannot load '$url' configuration.");
330             return false; 
331         }
332         return $this->doConfigureProperties($properties, $repository);
333     }
334
335
336     /**
337      * Read configuration options from <b>properties</b>.
338      *
339      * @see doConfigure().
340      * @param array $properties
341      * @param LoggerHierarchy &$hierarchy
342      */
343     function doConfigureProperties($properties, &$hierarchy)
344     {
345         $value = @$properties[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_DEBUG_KEY];
346         
347         if (!empty($value)) {
348             LoggerLog::internalDebugging(LoggerOptionConverter::toBoolean($value, LoggerLog::internalDebugging()));
349         }
350
351         $thresholdStr = @$properties[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_THRESHOLD_PREFIX];
352         $hierarchy->setThreshold(LoggerOptionConverter::toLevel($thresholdStr, LoggerLevel::getLevelAll()));
353         
354         $this->configureRootCategory($properties, $hierarchy);
355         $this->configureLoggerFactory($properties);
356         $this->parseCatsAndRenderers($properties, $hierarchy);
357
358         LoggerLog::debug("LoggerPropertyConfigurator::doConfigureProperties() Finished configuring.");
359         
360         return true;
361     }
362
363     // --------------------------------------------------------------------------
364     // Internal stuff
365     // --------------------------------------------------------------------------
366
367     /**
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}.
371      *  
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.
376      *
377      * @see parseCatsAndRenderers()
378      * @param array $props array of properties
379      */
380     function configureLoggerFactory($props)
381     {
382         $factoryFqcn = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_LOGGER_FACTORY_KEY];
383         if(!empty($factoryFqcn)) {
384             $factoryClassName = basename($factoryFqcn);
385             LoggerLog::debug(
386                 "LoggerPropertyConfigurator::configureLoggerFactory() Trying to load factory [" .
387                 $factoryClassName . 
388                 "]."
389             );
390             
391             if (!class_exists($factoryClassName))
392                 @include_once("{$factoryFqcn}.php");
393             if (class_exists($factoryClassName)) {
394                 $loggerFactory = new $factoryClassName();
395             } else {
396                 LoggerLog::debug(
397                     "LoggerPropertyConfigurator::configureLoggerFactory() Unable to load factory [" .
398                     $factoryClassName . 
399                     "]. Using default."
400                 );
401                 $loggerFactory = $this->loggerFactory;
402             }
403
404             LoggerLog::debug(
405                 "LoggerPropertyConfigurator::configureLoggerFactory() ".
406                 "Setting properties for category factory [" . get_class($loggerFactory) . "]."
407             );
408             
409             LoggerPropertySetter::setPropertiesByObject($loggerFactory, $props, LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_FACTORY_PREFIX . ".");
410         }
411     }
412     
413     /**
414      * @param array $props array of properties
415      * @param LoggerHierarchy &$hierarchy
416      */
417     function configureRootCategory($props, &$hierarchy)
418     {
419         $effectivePrefix = LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_LOGGER_PREFIX;
420         $value = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_LOGGER_PREFIX];
421
422         if(empty($value)) {
423             $value = @$props[LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_CATEGORY_PREFIX];
424             $effectivePrefix = LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ROOT_CATEGORY_PREFIX;
425         }
426
427         if (empty($value)) {
428             LoggerLog::debug(
429                 "LoggerPropertyConfigurator::configureRootCategory() ".
430                 "Could not find root logger information. Is this OK?"
431             );
432         } else {
433             $root =& $hierarchy->getRootLogger();
434             // synchronized(root) {
435                 $this->parseCategory(
436                 $props, 
437                 $root, 
438                 $effectivePrefix, 
439                 LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_INTERNAL_ROOT_NAME, 
440                 $value
441             );
442             // }
443         }
444     }
445
446     /**
447      * Parse non-root elements, such non-root categories and renderers.
448      *
449      * @param array $props array of properties
450      * @param LoggerHierarchy &$hierarchy
451      */
452     function parseCatsAndRenderers($props, &$hierarchy)
453     {
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));
461                 }
462                 $logger =& $hierarchy->getLogger($loggerName, $this->loggerFactory);
463                     // synchronized(logger) {
464                     $this->parseCategory($props, $logger, $key, $loggerName, $value);
465                     $this->parseAdditivityForLogger($props, $logger, $loggerName);
466                     // }
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);
472                 }
473                 }
474         }
475     }
476
477     /**
478      * Parse the additivity option for a non-root category.
479      *
480      * @param array $props array of properties
481      * @param Logger &$cat
482      * @param string $loggerName
483      */
484     function parseAdditivityForLogger($props, &$cat, $loggerName)
485     {
486         $value = LoggerOptionConverter::findAndSubst(
487                     LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ADDITIVITY_PREFIX . $loggerName,
488                                         $props
489                  );
490         LoggerLog::debug(
491             "LoggerPropertyConfigurator::parseAdditivityForLogger() ".
492             "Handling " . LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_ADDITIVITY_PREFIX . $loggerName . "=[{$value}]"
493         );
494         // touch additivity only if necessary
495         if(!empty($value)) {
496             $additivity = LoggerOptionConverter::toBoolean($value, true);
497             LoggerLog::debug(
498                 "LoggerPropertyConfigurator::parseAdditivityForLogger() ".
499                 "Setting additivity for [{$loggerName}] to [{$additivity}]"
500             );
501             $cat->setAdditivity($additivity);
502         }
503     }
504
505     /**
506      * This method must work for the root category as well.
507      *
508      * @param array $props array of properties
509      * @param Logger &$logger
510      * @param string $optionKey
511      * @param string $loggerName
512      * @param string $value
513      * @return Logger
514      */
515     //function &parseCategory($props, &$logger, $optionKey, $loggerName, $value)
516         function parseCategory($props, &$logger, $optionKey, $loggerName, $value)               // \8fC\90³(2006/12/12)
517     {
518         LoggerLog::debug(
519             "LoggerPropertyConfigurator::parseCategory() ".
520             "Parsing for [{$loggerName}] with value=[{$value}]."
521         );
522         
523         // We must skip over ',' but not white space
524         $st = explode(',', $value);
525
526         // If value is not in the form ", appender.." or "", then we should set
527         // the level of the loggeregory.
528
529         if(!(@$value{0} == ',' || empty($value))) {
530             // just to be on the safe side...
531             if(sizeof($st) == 0)
532                     return;
533                 
534             $levelStr = current($st);
535             LoggerLog::debug(
536                 "LoggerPropertyConfigurator::parseCategory() ".
537                 "Level token is [$levelStr]."
538             );
539
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
542             // root category.
543             if('INHERITED' == strtoupper($levelStr) || 'NULL' == strtoupper($levelStr)) {
544                     if ($loggerName == LOG4PHP_LOGGER_PROPERTY_CONFIGURATOR_INTERNAL_ROOT_NAME) {
545                     LoggerLog::warn(
546                         "LoggerPropertyConfigurator::parseCategory() ".
547                         "The root logger cannot be set to null."
548                     );
549                     } else {
550                         $logger->setLevel(null);
551                     }
552             } else {
553                     $logger->setLevel(LoggerOptionConverter::toLevel($levelStr, LoggerLevel::getLevelDebug()));
554             }
555         }
556
557         // Begin by removing all existing appenders.
558         $logger->removeAllAppenders();
559         while($appenderName = next($st)) {
560             $appenderName = trim($appenderName);
561             if(empty($appenderName))
562                 continue;
563             LoggerLog::debug(
564                 "LoggerPropertyConfigurator::parseCategory() ".
565                 "Parsing appender named [{$appenderName}]."
566             );
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);
571             }
572         }
573     }
574
575     /**
576      * @param array $props array of properties
577      * @param string $appenderName
578      * @return LoggerAppender
579      */
580     //function &parseAppender($props, $appenderName)
581     function parseAppender($props, $appenderName)               // \8fC\90³(2006/12/12)
582     {
583         //$appender =& LoggerAppender::singleton($appenderName);
584         $appender = LoggerAppender::singleton($appenderName);   // \8fC\90³(2006/12/12)
585         if($appender !== null) {
586             LoggerLog::debug(
587                 "LoggerPropertyConfigurator::parseAppender() ".
588                 "Appender [{$appenderName}] was already parsed."
589             );
590             return $appender;
591         }
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) {
600                 LoggerLog::warn(
601                     "LoggerPropertyConfigurator::parseAppender() ".
602                     "Could not instantiate appender named [$appenderName]."
603                 );
604                 return null;
605             }
606         } else {
607             LoggerLog::warn(
608                 "LoggerPropertyConfigurator::parseAppender() ".
609                 "Could not instantiate appender named [$appenderName] with null className."
610             );
611             return null;
612         }
613         
614         $appender->setName($appenderName);
615         if( $appender->requiresLayout() ) {
616             LoggerLog::debug(
617                 "LoggerPropertyConfigurator::parseAppender() ".
618                 "Parsing layout section for [$appenderName]."
619             );
620             $layoutClass = @$props[$layoutPrefix];
621             $layoutClass = LoggerOptionConverter::substVars($layoutClass, $props);
622             if (empty($layoutClass)) {
623                 LoggerLog::warn(
624                     "LoggerPropertyConfigurator::parseAppender() ".
625                     "layout class is empty in '$layoutPrefix'. Using Simple layout"
626                 );
627                 $layout = LoggerLayout::factory('LoggerLayoutSimple');
628             } else {
629                     $layout = LoggerLayout::factory($layoutClass);
630                 
631                     if($layout === null) {
632                         LoggerLog::warn(
633                         "LoggerPropertyConfigurator::parseAppender() ".
634                         "cannot create layout '$layoutClass'. Using Simple layout"
635                     );
636                     $layout = LoggerLayout::factory('LoggerLayoutSimple');
637                 }
638             }
639             
640             LoggerLog::debug(
641                 "LoggerPropertyConfigurator::parseAppender() ".
642                 "Parsing layout options for [$appenderName]."
643             );
644             LoggerPropertySetter::setPropertiesByObject($layout, $props, $layoutPrefix . ".");                
645             LoggerLog::debug(
646                 "LoggerPropertyConfigurator::parseAppender() ".
647                 "End Parsing layout options for [$appenderName]."
648             );
649             $appender->setLayout($layout);
650             
651         }
652         LoggerPropertySetter::setPropertiesByObject($appender, $props, $prefix . ".");
653         LoggerLog::debug(
654             "LoggerPropertyConfigurator::parseAppender() ".        
655             "Parsed [{$appenderName}] options."
656         );
657         return $appender;        
658     }
659
660 }
661 ?>