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: