| Code Coverage
| Classes and Traits | Functions and Methods | Lines
Total |  | 0.00% | 0 / 1 |  | 56.25% | 9 / 16 | CRAP |  | 91.49% | 172 / 188
LoggerConfiguratorDefault |  | 0.00% | 0 / 1 |  | 56.25% | 9 / 16 | 71.93 |  | 91.49% | 172 / 188
configure(LoggerHierarchy $hierarchy, $input = null) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 3 / 3
parse($input) |  | 0.00% | 0 / 1 | 5.01 |  | 93.33% | 14 / 15
getDefaultConfiguration() |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1
parseFile($url) |  | 100.00% | 1 / 1 | 2 |  | 100.00% | 6 / 6
getConfigType($url) |  | 0.00% | 0 / 1 | 5.12 |  | 83.33% | 10 / 12
doConfigure(LoggerHierarchy $hierarchy, $config) |  | 100.00% | 1 / 1 | 14 |  | 100.00% | 29 / 29
configureRenderer(LoggerHierarchy $hierarchy, $config) |  | 0.00% | 0 / 1 | 3.14 |  | 75.00% | 6 / 8
configureDefaultRenderer(LoggerHierarchy $hierarchy, $class) |  | 0.00% | 0 / 1 | 2.26 |  | 60.00% | 3 / 5
configureAppender($name, $config) |  | 0.00% | 0 / 1 | 13.23 |  | 88.89% | 32 / 36
createAppenderLayout(LoggerAppender $appender, $config) |  | 0.00% | 0 / 1 | 5.12 |  | 83.33% | 15 / 18
createAppenderFilter(LoggerAppender $appender, $config) |  | 0.00% | 0 / 1 | 4.04 |  | 86.67% | 13 / 15
configureRootLogger(LoggerHierarchy $hierarchy, $config) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 3 / 3
configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 3 / 3
configureLogger(Logger $logger, $config) |  | 100.00% | 1 / 1 | 8 |  | 100.00% | 23 / 23
setOptions($object, $options) |  | 100.00% | 1 / 1 | 3 |  | 100.00% | 9 / 9
warn($message) |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 2 / 2

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 | {
33 | /** XML configuration file format. */
34 | const FORMAT_XML = 'xml';
35 |
36 | /** PHP configuration file format. */
37 | const FORMAT_PHP = 'php';
38 |
39 | /** INI (properties) configuration file format. */
40 | const FORMAT_INI = 'ini';
41 |
42 | /** Defines which adapter should be used for parsing which format. */
43 | private $adapters = array(
44 | self::FORMAT_XML => 'LoggerConfigurationAdapterXML',
45 | self::FORMAT_INI => 'LoggerConfigurationAdapterINI',
46 | self::FORMAT_PHP => 'LoggerConfigurationAdapterPHP',
47 | );
48 |
49 | /** Default configuration; used if no configuration file is provided. */
50 | private static $defaultConfiguration = array(
51 | 'threshold' => 'ALL',
52 | 'rootLogger' => array(
53 | 'level' => 'DEBUG',
54 | 'appenders' => array('default'),
55 | ),
56 | 'appenders' => array(
57 | 'default' => array(
58 | 'class' => 'LoggerAppenderEcho'
59 | ),
60 | ),
61 | );
62 |
63 | /** Holds the appenders before they are linked to loggers. */
64 | private $appenders = array();
65 |
66 | /**
67 | * Configures log4php based on the given configuration. The input can
68 | * either be a path to the config file, or a PHP array holding the
69 | * configuration.
70 | *
71 | * If no configuration is given, or if the given configuration cannot be
72 | * parsed for whatever reason, a warning will be issued, and log4php
73 | * will use the default configuration contained in
74 | * {@link $defaultConfiguration}.
75 | *
76 | * @param LoggerHierarchy $hierarchy The hierarchy on which to perform
77 | * the configuration.
78 | * @param string|array $input Either path to the config file or the
79 | * configuration as an array. If not set, default configuration
80 | * will be used.
81 | */
82 | public function configure(LoggerHierarchy $hierarchy, $input = null) {
83 | $config = $this->parse($input);
84 | $this->doConfigure($hierarchy, $config);
85 | }
86 |
87 | /**
88 | * Parses the given configuration and returns the parsed configuration
89 | * as a PHP array. Does not perform any configuration.
90 | *
91 | * If no configuration is given, or if the given configuration cannot be
92 | * parsed for whatever reason, a warning will be issued, and the default
93 | * configuration will be returned ({@link $defaultConfiguration}).
94 | *
95 | * @param string|array $input Either path to the config file or the
96 | * configuration as an array. If not set, default configuration
97 | * will be used.
98 | * @return array The parsed configuration.
99 | */
100 | public function parse($input) {
101 | // No input - use default configuration
102 | if (!isset($input)) {
103 | $config = self::$defaultConfiguration;
104 | }
105 |
106 | // Array input - contains configuration within the array
107 | else if (is_array($input)) {
108 | $config = $input;
109 | }
110 |
111 | // String input - contains path to configuration file
112 | else if (is_string($input)) {
113 | try {
114 | $config = $this->parseFile($input);
115 | } catch (LoggerException $e) {
116 | $this->warn("Configuration failed. " . $e->getMessage() . " Using default configuration.");
117 | $config = self::$defaultConfiguration;
118 | }
119 | }
120 |
121 | // Anything else is an error
122 | else {
123 | $this->warn("Invalid configuration param given. Reverting to default configuration.");
124 | $config = self::$defaultConfiguration;
125 | }
126 |
127 | return $config;
128 | }
129 |
130 | /**
131 | * Returns the default log4php configuration.
132 | * @return array
133 | */
134 | public static function getDefaultConfiguration() {
135 | return self::$defaultConfiguration;
136 | }
137 |
138 | /**
139 | * Loads the configuration file from the given URL, determines which
140 | * adapter to use, converts the configuration to a PHP array and
141 | * returns it.
142 | *
143 | * @param string $url Path to the config file.
144 | * @return The configuration from the config file, as a PHP array.
145 | * @throws LoggerException If the configuration file cannot be loaded, or
146 | * if the parsing fails.
147 | */
148 | private function parseFile($url) {
149 |
150 | if (!file_exists($url)) {
151 | throw new LoggerException("File not found at [$url].");
152 | }
153 |
154 | $type = $this->getConfigType($url);
155 | $adapterClass = $this->adapters[$type];
156 |
157 | $adapter = new $adapterClass();
158 | return $adapter->convert($url);
159 | }
160 |
161 | /** Determines configuration file type based on the file extension. */
162 | private function getConfigType($url) {
163 | $info = pathinfo($url);
164 | $ext = strtolower($info['extension']);
165 |
166 | switch($ext) {
167 | case 'xml':
168 | return self::FORMAT_XML;
169 |
170 | case 'ini':
171 | case 'properties':
172 | return self::FORMAT_INI;
173 |
174 | case 'php':
175 | return self::FORMAT_PHP;
176 |
177 | default:
178 | throw new LoggerException("Unsupported configuration file extension: $ext");
179 | }
180 | }
181 |
182 | /**
183 | * Constructs the logger hierarchy based on configuration.
184 | *
185 | * @param LoggerHierarchy $hierarchy
186 | * @param array $config
187 | */
188 | private function doConfigure(LoggerHierarchy $hierarchy, $config) {
189 | if (isset($config['threshold'])) {
190 | $threshold = LoggerLevel::toLevel($config['threshold']);
191 | if (isset($threshold)) {
192 | $hierarchy->setThreshold($threshold);
193 | } else {
194 | $this->warn("Invalid threshold value [{$config['threshold']}] specified. Ignoring threshold definition.");
195 | }
196 | }
197 |
198 | // Configure appenders and add them to the appender pool
199 | if (isset($config['appenders']) && is_array($config['appenders'])) {
200 | foreach($config['appenders'] as $name => $appenderConfig) {
201 | $this->configureAppender($name, $appenderConfig);
202 | }
203 | }
204 |
205 | // Configure root logger
206 | if (isset($config['rootLogger'])) {
207 | $this->configureRootLogger($hierarchy, $config['rootLogger']);
208 | }
209 |
210 | // Configure loggers
211 | if (isset($config['loggers']) && is_array($config['loggers'])) {
212 | foreach($config['loggers'] as $loggerName => $loggerConfig) {
213 | $this->configureOtherLogger($hierarchy, $loggerName, $loggerConfig);
214 | }
215 | }
216 |
217 | // Configure renderers
218 | if (isset($config['renderers']) && is_array($config['renderers'])) {
219 | foreach($config['renderers'] as $rendererConfig) {
220 | $this->configureRenderer($hierarchy, $rendererConfig);
221 | }
222 | }
223 |
224 | if (isset($config['defaultRenderer'])) {
225 | $this->configureDefaultRenderer($hierarchy, $config['defaultRenderer']);
226 | }
227 | }
228 |
229 | private function configureRenderer(LoggerHierarchy $hierarchy, $config) {
230 | if (empty($config['renderingClass'])) {
231 | $this->warn("Rendering class not specified. Skipping renderer definition.");
232 | return;
233 | }
234 |
235 | if (empty($config['renderedClass'])) {
236 | $this->warn("Rendered class not specified. Skipping renderer definition.");
237 | return;
238 | }
239 |
240 | // Error handling performed by RendererMap
241 | $hierarchy->getRendererMap()->addRenderer($config['renderedClass'], $config['renderingClass']);
242 | }
243 |
244 | private function configureDefaultRenderer(LoggerHierarchy $hierarchy, $class) {
245 | if (empty($class)) {
246 | $this->warn("Rendering class not specified. Skipping default renderer definition.");
247 | return;
248 | }
249 |
250 | // Error handling performed by RendererMap
251 | $hierarchy->getRendererMap()->setDefaultRenderer($class);
252 | }
253 |
254 | /**
255 | * Configures an appender based on given config and saves it to
256 | * {@link $appenders} array so it can be later linked to loggers.
257 | * @param string $name Appender name.
258 | * @param array $config Appender configuration options.
259 | */
260 | private function configureAppender($name, $config) {
261 |
262 | // TODO: add this check to other places where it might be useful
263 | if (!is_array($config)) {
264 | $type = gettype($config);
265 | $this->warn("Invalid configuration provided for appender [$name]. Expected an array, found <$type>. Skipping appender definition.");
266 | return;
267 | }
268 |
269 | // Parse appender class
270 | $class = $config['class'];
271 | if (empty($class)) {
272 | $this->warn("No class given for appender [$name]. Skipping appender definition.");
273 | return;
274 | }
275 | if (!class_exists($class)) {
276 | $this->warn("Invalid class [$class] given for appender [$name]. Class does not exist. Skipping appender definition.");
277 | return;
278 | }
279 |
280 | // Instantiate the appender
281 | $appender = new $class($name);
282 | if (!($appender instanceof LoggerAppender)) {
283 | $this->warn("Invalid class [$class] given for appender [$name]. Not a valid LoggerAppender class. Skipping appender definition.");
284 | return;
285 | }
286 |
287 | // Parse the appender threshold
288 | if (isset($config['threshold'])) {
289 | $threshold = LoggerLevel::toLevel($config['threshold']);
290 | if ($threshold instanceof LoggerLevel) {
291 | $appender->setThreshold($threshold);
292 | } else {
293 | $this->warn("Invalid threshold value [{$config['threshold']}] specified for appender [$name]. Ignoring threshold definition.");
294 | }
295 | }
296 |
297 | // Parse the appender layout
298 | if ($appender->requiresLayout() && isset($config['layout'])) {
299 | $this->createAppenderLayout($appender, $config['layout']);
300 | }
301 |
302 | // Parse filters
303 | if (isset($config['filters']) && is_array($config['filters'])) {
304 | foreach($config['filters'] as $filterConfig) {
305 | $this->createAppenderFilter($appender, $filterConfig);
306 | }
307 | }
308 |
309 | // Set options if any
310 | if (isset($config['params'])) {
311 | $this->setOptions($appender, $config['params']);
312 | }
313 |
314 | // Activate and save for later linking to loggers
315 | $appender->activateOptions();
316 | $this->appenders[$name] = $appender;
317 | }
318 |
319 | /**
320 | * Parses layout config, creates the layout and links it to the appender.
321 | * @param LoggerAppender $appender
322 | * @param array $config Layout configuration.
323 | */
324 | private function createAppenderLayout(LoggerAppender $appender, $config) {
325 | $name = $appender->getName();
326 | $class = $config['class'];
327 | if (empty($class)) {
328 | $this->warn("Layout class not specified for appender [$name]. Reverting to default layout.");
329 | return;
330 | }
331 | if (!class_exists($class)) {
332 | $this->warn("Nonexistant layout class [$class] specified for appender [$name]. Reverting to default layout.");
333 | return;
334 | }
335 |
336 | $layout = new $class();
337 | if (!($layout instanceof LoggerLayout)) {
338 | $this->warn("Invalid layout class [$class] sepcified for appender [$name]. Reverting to default layout.");
339 | return;
340 | }
341 |
342 | if (isset($config['params'])) {
343 | $this->setOptions($layout, $config['params']);
344 | }
345 |
346 | $layout->activateOptions();
347 | $appender->setLayout($layout);
348 | }
349 |
350 | /**
351 | * Parses filter config, creates the filter and adds it to the appender's
352 | * filter chain.
353 | * @param LoggerAppender $appender
354 | * @param array $config Filter configuration.
355 | */
356 | private function createAppenderFilter(LoggerAppender $appender, $config) {
357 | $name = $appender->getName();
358 | $class = $config['class'];
359 | if (!class_exists($class)) {
360 | $this->warn("Nonexistant filter class [$class] specified on appender [$name]. Skipping filter definition.");
361 | return;
362 | }
363 |
364 | $filter = new $class();
365 | if (!($filter instanceof LoggerFilter)) {
366 | $this->warn("Invalid filter class [$class] sepcified on appender [$name]. Skipping filter definition.");
367 | return;
368 | }
369 |
370 | if (isset($config['params'])) {
371 | $this->setOptions($filter, $config['params']);
372 | }
373 |
374 | $filter->activateOptions();
375 | $appender->addFilter($filter);
376 | }
377 |
378 | /**
379 | * Configures the root logger
380 | * @see configureLogger()
381 | */
382 | private function configureRootLogger(LoggerHierarchy $hierarchy, $config) {
383 | $logger = $hierarchy->getRootLogger();
384 | $this->configureLogger($logger, $config);
385 | }
386 |
387 | /**
388 | * Configures a logger which is not root.
389 | * @see configureLogger()
390 | */
391 | private function configureOtherLogger(LoggerHierarchy $hierarchy, $name, $config) {
392 | // Get logger from hierarchy (this creates it if it doesn't already exist)
393 | $logger = $hierarchy->getLogger($name);
394 | $this->configureLogger($logger, $config);
395 | }
396 |
397 | /**
398 | * Configures a logger.
399 | *
400 | * @param Logger $logger The logger to configure
401 | * @param array $config Logger configuration options.
402 | */
403 | private function configureLogger(Logger $logger, $config) {
404 | $loggerName = $logger->getName();
405 |
406 | // Set logger level
407 | if (isset($config['level'])) {
408 | $level = LoggerLevel::toLevel($config['level']);
409 | if (isset($level)) {
410 | $logger->setLevel($level);
411 | } else {
412 | $this->warn("Invalid level value [{$config['level']}] specified for logger [$loggerName]. Ignoring level definition.");
413 | }
414 | }
415 |
416 | // Link appenders to logger
417 | if (isset($config['appenders'])) {
418 | foreach($config['appenders'] as $appenderName) {
419 | if (isset($this->appenders[$appenderName])) {
420 | $logger->addAppender($this->appenders[$appenderName]);
421 | } else {
422 | $this->warn("Nonexistnant appender [$appenderName] linked to logger [$loggerName].");
423 | }
424 | }
425 | }
426 |
427 | // Set logger additivity
428 | if (isset($config['additivity'])) {
429 | try {
430 | $additivity = LoggerOptionConverter::toBooleanEx($config['additivity'], null);
431 | $logger->setAdditivity($additivity);
432 | } catch (Exception $ex) {
433 | $this->warn("Invalid additivity value [{$config['additivity']}] specified for logger [$loggerName]. Ignoring additivity setting.");
434 | }
435 | }
436 | }
437 |
438 | /**
439 | * Helper method which applies given options to an object which has setters
440 | * for these options (such as appenders, layouts, etc.).
441 | *
442 | * For example, if options are:
443 | * <code>
444 | * array(
445 | * 'file' => '/tmp/myfile.log',
446 | * 'append' => true
447 | * )
448 | * </code>
449 | *
450 | * This method will call:
451 | * <code>
452 | * $object->setFile('/tmp/myfile.log')
453 | * $object->setAppend(true)
454 | * </code>
455 | *
456 | * If required setters do not exist, it will produce a warning.
457 | *
458 | * @param mixed $object The object to configure.
459 | * @param unknown_type $options
460 | */
461 | private function setOptions($object, $options) {
462 | foreach($options as $name => $value) {
463 | $setter = "set$name";
464 | if (method_exists($object, $setter)) {
465 | $object->$setter($value);
466 | } else {
467 | $class = get_class($object);
468 | $this->warn("Nonexistant option [$name] specified on [$class]. Skipping.");
469 | }
470 | }
471 | }
472 |
473 | /** Helper method to simplify error reporting. */
474 | private function warn($message) {
475 | trigger_error("log4php: $message", E_USER_WARNING);
476 | }
477 | }