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: * Converts ini configuration files to a PHP array.
23: *
24: * These used to be called "properties" files (inherited from log4j), and that
25: * file extension is still supported.
26: *
27: * @package log4php
28: * @subpackage configurators
29: * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
30: * @version $Revision: 1343601 $
31: * @since 2.2
32: */
33: class LoggerConfigurationAdapterINI implements LoggerConfigurationAdapter {
34:
35: /** Name to assign to the root logger. */
36: const ROOT_LOGGER_NAME = "root";
37:
38: /** Prefix used for defining logger additivity. */
39: const ADDITIVITY_PREFIX = "log4php.additivity.";
40:
41: /** Prefix used for defining logger threshold. */
42: const THRESHOLD_PREFIX = "log4php.threshold";
43:
44: /** Prefix used for defining the root logger. */
45: const ROOT_LOGGER_PREFIX = "log4php.rootLogger";
46:
47: /** Prefix used for defining a logger. */
48: const LOGGER_PREFIX = "log4php.logger.";
49:
50: /** Prefix used for defining an appender. */
51: const APPENDER_PREFIX = "log4php.appender.";
52:
53: /** Prefix used for defining a renderer. */
54: const RENDERER_PREFIX = "log4php.renderer.";
55:
56: /** Holds the configuration. */
57: private $config = array();
58:
59: /**
60: * Loads and parses the INI configuration file.
61: *
62: * @param string $url Path to the config file.
63: * @throws LoggerException
64: */
65: private function load($url) {
66: if (!file_exists($url)) {
67: throw new LoggerException("File [$url] does not exist.");
68: }
69:
70: $properties = @parse_ini_file($url, true);
71: if ($properties === false) {
72: $error = error_get_last();
73: throw new LoggerException("Error parsing configuration file: {$error['message']}");
74: }
75:
76: return $properties;
77: }
78:
79: /**
80: * Converts the provided INI configuration file to a PHP array config.
81: *
82: * @param string $path Path to the config file.
83: * @throws LoggerException If the file cannot be loaded or parsed.
84: */
85: public function convert($path) {
86: // Load the configuration
87: $properties = $this->load($path);
88:
89: // Parse threshold
90: if (isset($properties[self::THRESHOLD_PREFIX])) {
91: $this->config['threshold'] = $properties[self::THRESHOLD_PREFIX];
92: }
93:
94: // Parse root logger
95: if (isset($properties[self::ROOT_LOGGER_PREFIX])) {
96: $this->parseLogger($properties[self::ROOT_LOGGER_PREFIX], self::ROOT_LOGGER_NAME);
97: }
98:
99: $appenders = array();
100:
101: foreach($properties as $key => $value) {
102: // Parse loggers
103: if ($this->beginsWith($key, self::LOGGER_PREFIX)) {
104: $name = substr($key, strlen(self::LOGGER_PREFIX));
105: $this->parseLogger($value, $name);
106: }
107:
108: // Parse additivity
109: if ($this->beginsWith($key, self::ADDITIVITY_PREFIX)) {
110: $name = substr($key, strlen(self::ADDITIVITY_PREFIX));
111: $this->config['loggers'][$name]['additivity'] = $value;
112: }
113:
114: // Parse appenders
115: else if ($this->beginsWith($key, self::APPENDER_PREFIX)) {
116: $this->parseAppender($key, $value);
117: }
118:
119: // Parse renderers
120: else if ($this->beginsWith($key, self::RENDERER_PREFIX)) {
121: $this->parseRenderer($key, $value);
122: }
123: }
124:
125: return $this->config;
126: }
127:
128:
129: /**
130: * Parses a logger definition.
131: *
132: * Loggers are defined in the following manner:
133: * <pre>
134: * log4php.logger.<name> = [<level>], [<appender-ref>, <appender-ref>, ...]
135: * </pre>
136: *
137: * @param string $value The configuration value (level and appender-refs).
138: * @param string $name Logger name.
139: */
140: private function parseLogger($value, $name) {
141: // Value is divided by commas
142: $parts = explode(',', $value);
143: if (empty($value) || empty($parts)) {
144: return;
145: }
146:
147: // The first value is the logger level
148: $level = array_shift($parts);
149:
150: // The remaining values are appender references
151: $appenders = array();
152: while($appender = array_shift($parts)) {
153: $appender = trim($appender);
154: if (!empty($appender)) {
155: $appenders[] = trim($appender);
156: }
157: }
158:
159: // Find the target configuration
160: if ($name == self::ROOT_LOGGER_NAME) {
161: $this->config['rootLogger']['level'] = trim($level);
162: $this->config['rootLogger']['appenders'] = $appenders;
163: } else {
164: $this->config['loggers'][$name]['level'] = trim($level);
165: $this->config['loggers'][$name]['appenders'] = $appenders;
166: }
167: }
168:
169: /**
170: * Parses an configuration line pertaining to an appender.
171: *
172: * Parses the following patterns:
173: *
174: * Appender class:
175: * <pre>
176: * log4php.appender.<name> = <class>
177: * </pre>
178: *
179: * Appender parameter:
180: * <pre>
181: * log4php.appender.<name>.<param> = <value>
182: * </pre>
183: *
184: * Appender threshold:
185: * <pre>
186: * log4php.appender.<name>.threshold = <level>
187: * </pre>
188: *
189: * Appender layout:
190: * <pre>
191: * log4php.appender.<name>.layout = <layoutClass>
192: * </pre>
193: *
194: * Layout parameter:
195: * <pre>
196: * log4php.appender.<name>.layout.<param> = <value>
197: * </pre>
198: *
199: * For example, a full appender config might look like:
200: * <pre>
201: * log4php.appender.myAppender = LoggerAppenderConsole
202: * log4php.appender.myAppender.threshold = info
203: * log4php.appender.myAppender.target = stdout
204: * log4php.appender.myAppender.layout = LoggerLayoutPattern
205: * log4php.appender.myAppender.layout.conversionPattern = "%d %c: %m%n"
206: * </pre>
207: *
208: * After parsing all these options, the following configuration can be
209: * found under $this->config['appenders']['myAppender']:
210: * <pre>
211: * array(
212: * 'class' => LoggerAppenderConsole,
213: * 'threshold' => info,
214: * 'params' => array(
215: * 'target' => 'stdout'
216: * ),
217: * 'layout' => array(
218: * 'class' => 'LoggerAppenderConsole',
219: * 'params' => array(
220: * 'conversionPattern' => '%d %c: %m%n'
221: * )
222: * )
223: * )
224: * </pre>
225: *
226: * @param string $key
227: * @param string $value
228: */
229: private function parseAppender($key, $value) {
230:
231: // Remove the appender prefix from key
232: $subKey = substr($key, strlen(self::APPENDER_PREFIX));
233:
234: // Divide the string by dots
235: $parts = explode('.', $subKey);
236: $count = count($parts);
237:
238: // The first part is always the appender name
239: $name = trim($parts[0]);
240:
241: // Only one part - this line defines the appender class
242: if ($count == 1) {
243: $this->config['appenders'][$name]['class'] = $value;
244: return;
245: }
246:
247: // Two parts - either a parameter, a threshold or layout class
248: else if ($count == 2) {
249:
250: if ($parts[1] == 'layout') {
251: $this->config['appenders'][$name]['layout']['class'] = $value;
252: return;
253: } else if ($parts[1] == 'threshold') {
254: $this->config['appenders'][$name]['threshold'] = $value;
255: return;
256: } else {
257: $this->config['appenders'][$name]['params'][$parts[1]] = $value;
258: return;
259: }
260: }
261:
262: // Three parts - this can only be a layout parameter
263: else if ($count == 3) {
264: if ($parts[1] == 'layout') {
265: $this->config['appenders'][$name]['layout']['params'][$parts[2]] = $value;
266: return;
267: }
268: }
269:
270: trigger_error("log4php: Don't know how to parse the following line: \"$key = $value\". Skipping.");
271: }
272:
273: /**
274: * Parses a renderer definition.
275: *
276: * Renderers are defined as:
277: * <pre>
278: * log4php.renderer.<renderedClass> = <renderingClass>
279: * </pre>
280: *
281: * @param string $key log4php.renderer.<renderedClass>
282: * @param string $value <renderingClass>
283: */
284: private function parseRenderer($key, $value) {
285: // Remove the appender prefix from key
286: $renderedClass = substr($key, strlen(self::APPENDER_PREFIX));
287: $renderingClass = $value;
288:
289: $this->config['renderers'][] = compact('renderedClass', 'renderingClass');
290: }
291:
292: /** Helper method. Returns true if $str begins with $sub. */
293: private function beginsWith($str, $sub) {
294: return (strncmp($str, $sub, strlen($sub)) == 0);
295: }
296:
297:
298: }
299:
300: