1 <?php
2 /**
3 * Copyright 2012-2014 Rackspace US, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 namespace OpenCloud\Common\Log;
19
20 use OpenCloud\Common\Exceptions\LoggingException;
21 use Psr\Log\AbstractLogger;
22 use Psr\Log\LogLevel;
23
24 /**
25 * Basic logger for OpenCloud which extends FIG's PSR-3 standard logger.
26 *
27 * @link https://github.com/php-fig/log
28 */
29 class Logger extends AbstractLogger
30 {
31 /**
32 * Is this debug class enabled or not?
33 *
34 * @var bool
35 */
36 private $enabled;
37
38 /**
39 * These are the levels which will always be outputted - regardless of
40 * user-imposed settings.
41 *
42 * @var array
43 */
44 private $urgentLevels = array(
45 LogLevel::EMERGENCY,
46 LogLevel::ALERT,
47 LogLevel::CRITICAL
48 );
49
50 /**
51 * Logging options.
52 *
53 * @var array
54 */
55 private $options = array(
56 'outputToFile' => false,
57 'logFile' => null,
58 'dateFormat' => 'd/m/y H:I',
59 'delimeter' => ' - '
60 );
61
62 public function __construct($enabled = false)
63 {
64 $this->enabled = $enabled;
65 }
66
67 public static function newInstance()
68 {
69 return new static();
70 }
71
72 /**
73 * Determines whether a log level needs to be outputted.
74 *
75 * @param string $logLevel
76 * @return bool
77 */
78 private function outputIsUrgent($logLevel)
79 {
80 return in_array($logLevel, $this->urgentLevels);
81 }
82
83 /**
84 * Interpolates context values into the message placeholders.
85 *
86 * @param string $message
87 * @param array $context
88 * @return type
89 */
90 private function interpolate($message, array $context = array())
91 {
92 // build a replacement array with braces around the context keys
93 $replace = array();
94 foreach ($context as $key => $val) {
95 $replace['{' . $key . '}'] = $val;
96 }
97
98 // interpolate replacement values into the message and return
99 return strtr($message, $replace);
100 }
101
102 /**
103 * Enable or disable the debug class.
104 *
105 * @param bool $enabled
106 * @return self
107 */
108 public function setEnabled($enabled)
109 {
110 $this->enabled = $enabled;
111
112 return $this;
113 }
114
115 /**
116 * Is the debug class enabled?
117 *
118 * @return bool
119 */
120 public function isEnabled()
121 {
122 return $this->enabled === true;
123 }
124
125 /**
126 * Set an array of options.
127 *
128 * @param array $options
129 */
130 public function setOptions(array $options = array())
131 {
132 foreach ($options as $key => $value) {
133 $this->setOption($key, $value);
134 }
135
136 return $this;
137 }
138
139 /**
140 * Get all options.
141 *
142 * @return array
143 */
144 public function getOptions()
145 {
146 return $this->options;
147 }
148
149 /**
150 * Set an individual option.
151 *
152 * @param string $key
153 * @param string $value
154 */
155 public function setOption($key, $value)
156 {
157 if ($this->optionExists($key)) {
158 $this->options[$key] = $value;
159
160 return $this;
161 }
162 }
163
164 /**
165 * Get an individual option.
166 *
167 * @param string $key
168 * @return string|null
169 */
170 public function getOption($key)
171 {
172 if ($this->optionExists($key)) {
173 return $this->options[$key];
174 }
175 }
176
177 /**
178 * Check whether an individual option exists.
179 *
180 * @param string $key
181 * @return bool
182 */
183 private function optionExists($key)
184 {
185 return array_key_exists($key, $this->getOptions());
186 }
187
188 /**
189 * Outputs a log message if necessary.
190 *
191 * @param string $logLevel
192 * @param string $message
193 * @param string $context
194 */
195 public function log($level, $message, array $context = array())
196 {
197 if ($this->outputIsUrgent($level) || $this->isEnabled()) {
198 $this->dispatch($message, $context);
199 }
200 }
201
202 /**
203 * Used to format the line outputted in the log file.
204 *
205 * @param string $string
206 * @return string
207 */
208 private function formatFileLine($string)
209 {
210 $format = $this->getOption('dateFormat') . $this->getOption('delimeter');
211
212 return date($format) . $string;
213 }
214
215 /**
216 * Dispatch a log output message.
217 *
218 * @param string $message
219 * @param array $context
220 * @throws LoggingException
221 */
222 private function dispatch($message, $context)
223 {
224 $output = $this->interpolate($message, $context) . PHP_EOL;
225
226 if ($this->getOption('outputToFile') === true) {
227 $file = $this->getOption('logFile');
228
229 if (!is_writable($file)) {
230 throw new LoggingException(
231 'The log file either does not exist or is not writeable'
232 );
233 }
234
235 // Output to file
236 file_put_contents($file, $this->formatFileLine($output), FILE_APPEND);
237 } else {
238 echo $output;
239 }
240 }
241
242 /**
243 * Helper method, use PSR-3 warning function for deprecation warnings
244 * @see http://www.php-fig.org/psr/psr-3/
245 */
246 public static function deprecated($method, $new)
247 {
248 return sprintf('The %s method is deprecated, please use %s instead', $method, $new);
249 }
250 }
251