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$
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 13 : if (!file_exists($url)) {
67 1 : throw new LoggerException("File [$url] does not exist.");
68 : }
69 :
70 12 : $properties = @parse_ini_file($url, true);
71 12 : if ($properties === false) {
72 1 : $error = error_get_last();
73 1 : throw new LoggerException("Error parsing configuration file: {$error['message']}");
74 : }
75 :
76 11 : 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 13 : $properties = $this->load($path);
88 :
89 : // Parse threshold
90 11 : if (isset($properties[self::THRESHOLD_PREFIX])) {
91 6 : $this->config['threshold'] = $properties[self::THRESHOLD_PREFIX];
92 6 : }
93 :
94 : // Parse root logger
95 11 : if (isset($properties[self::ROOT_LOGGER_PREFIX])) {
96 11 : $this->parseLogger($properties[self::ROOT_LOGGER_PREFIX], self::ROOT_LOGGER_NAME);
97 11 : }
98 :
99 11 : $appenders = array();
100 :
101 11 : foreach($properties as $key => $value) {
102 : // Parse loggers
103 11 : if ($this->beginsWith($key, self::LOGGER_PREFIX)) {
104 4 : $name = substr($key, strlen(self::LOGGER_PREFIX));
105 4 : $this->parseLogger($value, $name);
106 4 : }
107 :
108 : // Parse additivity
109 11 : if ($this->beginsWith($key, self::ADDITIVITY_PREFIX)) {
110 4 : $name = substr($key, strlen(self::ADDITIVITY_PREFIX));
111 4 : $this->config['loggers'][$name]['additivity'] = $value;
112 4 : }
113 :
114 : // Parse appenders
115 11 : else if ($this->beginsWith($key, self::APPENDER_PREFIX)) {
116 11 : $this->parseAppender($key, $value);
117 11 : }
118 :
119 : // Parse renderers
120 11 : else if ($this->beginsWith($key, self::RENDERER_PREFIX)) {
121 6 : $this->parseRenderer($key, $value);
122 6 : }
123 11 : }
124 :
125 9 : 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 11 : $parts = explode(',', $value);
143 11 : if (empty($value) || empty($parts)) {
144 0 : return;
145 : }
146 :
147 : // The first value is the logger level
148 11 : $level = array_shift($parts);
149 :
150 : // The remaining values are appender references
151 11 : $appenders = array();
152 11 : while($appender = array_shift($parts)) {
153 11 : $appender = trim($appender);
154 11 : if (!empty($appender)) {
155 11 : $appenders[] = trim($appender);
156 11 : }
157 11 : }
158 :
159 : // Find the target configuration
160 11 : if ($name == self::ROOT_LOGGER_NAME) {
161 11 : $this->config['rootLogger']['level'] = trim($level);
162 11 : $this->config['rootLogger']['appenders'] = $appenders;
163 11 : } else {
164 4 : $this->config['loggers'][$name]['level'] = trim($level);
165 4 : $this->config['loggers'][$name]['appenders'] = $appenders;
166 : }
167 11 : }
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 11 : $subKey = substr($key, strlen(self::APPENDER_PREFIX));
233 :
234 : // Divide the string by dots
235 11 : $parts = explode('.', $subKey);
236 11 : $count = count($parts);
237 :
238 : // The first part is always the appender name
239 11 : $name = trim($parts[0]);
240 :
241 : // Only one part - this line defines the appender class
242 11 : if ($count == 1) {
243 11 : $this->config['appenders'][$name]['class'] = $value;
244 11 : return;
245 : }
246 :
247 : // Two parts - either a parameter, a threshold or layout class
248 11 : else if ($count == 2) {
249 :
250 9 : if ($parts[1] == 'layout') {
251 9 : $this->config['appenders'][$name]['layout']['class'] = $value;
252 9 : return;
253 6 : } else if ($parts[1] == 'threshold') {
254 6 : $this->config['appenders'][$name]['threshold'] = $value;
255 6 : return;
256 : } else {
257 1 : $this->config['appenders'][$name]['params'][$parts[1]] = $value;
258 1 : return;
259 : }
260 : }
261 :
262 : // Three parts - this can only be a layout parameter
263 3 : else if ($count == 3) {
264 2 : if ($parts[1] == 'layout') {
265 1 : $this->config['appenders'][$name]['layout']['params'][$parts[2]] = $value;
266 1 : return;
267 : }
268 1 : }
269 :
270 2 : trigger_error("log4php: Don't know how to parse the following line: \"$key = $value\". Skipping.");
271 0 : }
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 6 : $renderedClass = substr($key, strlen(self::APPENDER_PREFIX));
287 6 : $renderingClass = $value;
288 :
289 6 : $this->config['renderers'][] = compact('renderedClass', 'renderingClass');
290 6 : }
291 :
292 : /** Helper method. Returns true if $str begins with $sub. */
293 : private function beginsWith($str, $sub) {
294 11 : return (strncmp($str, $sub, strlen($sub)) == 0);
295 : }
296 :
297 :
298 : }
299 :
|