|
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 } |