1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * Redistributions of files must retain the above copyright notice.
8: *
9: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10: * @link https://cakephp.org CakePHP(tm) Project
11: * @since 0.2.9
12: * @license https://opensource.org/licenses/mit-license.php MIT License
13: */
14: namespace Cake\Log;
15:
16: use Cake\Core\StaticConfigTrait;
17: use Cake\Log\Engine\BaseLog;
18: use InvalidArgumentException;
19:
20: /**
21: * Logs messages to configured Log adapters. One or more adapters
22: * can be configured using Cake Logs's methods. If you don't
23: * configure any adapters, and write to Log, the messages will be
24: * ignored.
25: *
26: * ### Configuring Log adapters
27: *
28: * You can configure log adapters in your applications `config/app.php` file.
29: * A sample configuration would look like:
30: *
31: * ```
32: * Log::setConfig('my_log', ['className' => 'FileLog']);
33: * ```
34: *
35: * You can define the className as any fully namespaced classname or use a short hand
36: * classname to use loggers in the `App\Log\Engine` & `Cake\Log\Engine` namespaces.
37: * You can also use plugin short hand to use logging classes provided by plugins.
38: *
39: * Log adapters are required to implement `Psr\Log\LoggerInterface`, and there is a
40: * built-in base class (`Cake\Log\Engine\BaseLog`) that can be used for custom loggers.
41: *
42: * Outside of the `className` key, all other configuration values will be passed to the
43: * logging adapter's constructor as an array.
44: *
45: * ### Logging levels
46: *
47: * When configuring loggers, you can set which levels a logger will handle.
48: * This allows you to disable debug messages in production for example:
49: *
50: * ```
51: * Log::setConfig('default', [
52: * 'className' => 'File',
53: * 'path' => LOGS,
54: * 'levels' => ['error', 'critical', 'alert', 'emergency']
55: * ]);
56: * ```
57: *
58: * The above logger would only log error messages or higher. Any
59: * other log messages would be discarded.
60: *
61: * ### Logging scopes
62: *
63: * When configuring loggers you can define the active scopes the logger
64: * is for. If defined, only the listed scopes will be handled by the
65: * logger. If you don't define any scopes an adapter will catch
66: * all scopes that match the handled levels.
67: *
68: * ```
69: * Log::setConfig('payments', [
70: * 'className' => 'File',
71: * 'scopes' => ['payment', 'order']
72: * ]);
73: * ```
74: *
75: * The above logger will only capture log entries made in the
76: * `payment` and `order` scopes. All other scopes including the
77: * undefined scope will be ignored.
78: *
79: * ### Writing to the log
80: *
81: * You write to the logs using Log::write(). See its documentation for more information.
82: *
83: * ### Logging Levels
84: *
85: * By default Cake Log supports all the log levels defined in
86: * RFC 5424. When logging messages you can either use the named methods,
87: * or the correct constants with `write()`:
88: *
89: * ```
90: * Log::error('Something horrible happened');
91: * Log::write(LOG_ERR, 'Something horrible happened');
92: * ```
93: *
94: * ### Logging scopes
95: *
96: * When logging messages and configuring log adapters, you can specify
97: * 'scopes' that the logger will handle. You can think of scopes as subsystems
98: * in your application that may require different logging setups. For
99: * example in an e-commerce application you may want to handle logged errors
100: * in the cart and ordering subsystems differently than the rest of the
101: * application. By using scopes you can control logging for each part
102: * of your application and also use standard log levels.
103: */
104: class Log
105: {
106: use StaticConfigTrait {
107: setConfig as protected _setConfig;
108: }
109:
110: /**
111: * An array mapping url schemes to fully qualified Log engine class names
112: *
113: * @var array
114: */
115: protected static $_dsnClassMap = [
116: 'console' => 'Cake\Log\Engine\ConsoleLog',
117: 'file' => 'Cake\Log\Engine\FileLog',
118: 'syslog' => 'Cake\Log\Engine\SyslogLog',
119: ];
120:
121: /**
122: * Internal flag for tracking whether or not configuration has been changed.
123: *
124: * @var bool
125: */
126: protected static $_dirtyConfig = false;
127:
128: /**
129: * LogEngineRegistry class
130: *
131: * @var \Cake\Log\LogEngineRegistry|null
132: */
133: protected static $_registry;
134:
135: /**
136: * Handled log levels
137: *
138: * @var array
139: */
140: protected static $_levels = [
141: 'emergency',
142: 'alert',
143: 'critical',
144: 'error',
145: 'warning',
146: 'notice',
147: 'info',
148: 'debug'
149: ];
150:
151: /**
152: * Log levels as detailed in RFC 5424
153: * https://tools.ietf.org/html/rfc5424
154: *
155: * @var array
156: */
157: protected static $_levelMap = [
158: 'emergency' => LOG_EMERG,
159: 'alert' => LOG_ALERT,
160: 'critical' => LOG_CRIT,
161: 'error' => LOG_ERR,
162: 'warning' => LOG_WARNING,
163: 'notice' => LOG_NOTICE,
164: 'info' => LOG_INFO,
165: 'debug' => LOG_DEBUG,
166: ];
167:
168: /**
169: * Initializes registry and configurations
170: *
171: * @return void
172: */
173: protected static function _init()
174: {
175: if (empty(static::$_registry)) {
176: static::$_registry = new LogEngineRegistry();
177: }
178: if (static::$_dirtyConfig) {
179: static::_loadConfig();
180: }
181: static::$_dirtyConfig = false;
182: }
183:
184: /**
185: * Load the defined configuration and create all the defined logging
186: * adapters.
187: *
188: * @return void
189: */
190: protected static function _loadConfig()
191: {
192: foreach (static::$_config as $name => $properties) {
193: if (isset($properties['engine'])) {
194: $properties['className'] = $properties['engine'];
195: }
196: if (!static::$_registry->has($name)) {
197: static::$_registry->load($name, $properties);
198: }
199: }
200: }
201:
202: /**
203: * Reset all the connected loggers. This is useful to do when changing the logging
204: * configuration or during testing when you want to reset the internal state of the
205: * Log class.
206: *
207: * Resets the configured logging adapters, as well as any custom logging levels.
208: * This will also clear the configuration data.
209: *
210: * @return void
211: */
212: public static function reset()
213: {
214: static::$_registry = null;
215: static::$_config = [];
216: static::$_dirtyConfig = true;
217: }
218:
219: /**
220: * Gets log levels
221: *
222: * Call this method to obtain current
223: * level configuration.
224: *
225: * @return array active log levels
226: */
227: public static function levels()
228: {
229: return static::$_levels;
230: }
231:
232: /**
233: * This method can be used to define logging adapters for an application
234: * or read existing configuration.
235: *
236: * To change an adapter's configuration at runtime, first drop the adapter and then
237: * reconfigure it.
238: *
239: * Loggers will not be constructed until the first log message is written.
240: *
241: * ### Usage
242: *
243: * Setting a cache engine up.
244: *
245: * ```
246: * Log::setConfig('default', $settings);
247: * ```
248: *
249: * Injecting a constructed adapter in:
250: *
251: * ```
252: * Log::setConfig('default', $instance);
253: * ```
254: *
255: * Using a factory function to get an adapter:
256: *
257: * ```
258: * Log::setConfig('default', function () { return new FileLog(); });
259: * ```
260: *
261: * Configure multiple adapters at once:
262: *
263: * ```
264: * Log::setConfig($arrayOfConfig);
265: * ```
266: *
267: * @param string|array $key The name of the logger config, or an array of multiple configs.
268: * @param array|null $config An array of name => config data for adapter.
269: * @return void
270: * @throws \BadMethodCallException When trying to modify an existing config.
271: */
272: public static function setConfig($key, $config = null)
273: {
274: static::_setConfig($key, $config);
275: static::$_dirtyConfig = true;
276: }
277:
278: /**
279: * Get a logging engine.
280: *
281: * @param string $name Key name of a configured adapter to get.
282: * @return \Cake\Log\Engine\BaseLog|false Instance of BaseLog or false if not found
283: */
284: public static function engine($name)
285: {
286: static::_init();
287: if (static::$_registry->{$name}) {
288: return static::$_registry->{$name};
289: }
290:
291: return false;
292: }
293:
294: /**
295: * Writes the given message and type to all of the configured log adapters.
296: * Configured adapters are passed both the $level and $message variables. $level
297: * is one of the following strings/values.
298: *
299: * ### Levels:
300: *
301: * - `LOG_EMERG` => 'emergency',
302: * - `LOG_ALERT` => 'alert',
303: * - `LOG_CRIT` => 'critical',
304: * - `LOG_ERR` => 'error',
305: * - `LOG_WARNING` => 'warning',
306: * - `LOG_NOTICE` => 'notice',
307: * - `LOG_INFO` => 'info',
308: * - `LOG_DEBUG` => 'debug',
309: *
310: * ### Basic usage
311: *
312: * Write a 'warning' message to the logs:
313: *
314: * ```
315: * Log::write('warning', 'Stuff is broken here');
316: * ```
317: *
318: * ### Using scopes
319: *
320: * When writing a log message you can define one or many scopes for the message.
321: * This allows you to handle messages differently based on application section/feature.
322: *
323: * ```
324: * Log::write('warning', 'Payment failed', ['scope' => 'payment']);
325: * ```
326: *
327: * When configuring loggers you can configure the scopes a particular logger will handle.
328: * When using scopes, you must ensure that the level of the message, and the scope of the message
329: * intersect with the defined levels & scopes for a logger.
330: *
331: * ### Unhandled log messages
332: *
333: * If no configured logger can handle a log message (because of level or scope restrictions)
334: * then the logged message will be ignored and silently dropped. You can check if this has happened
335: * by inspecting the return of write(). If false the message was not handled.
336: *
337: * @param int|string $level The severity level of the message being written.
338: * The value must be an integer or string matching a known level.
339: * @param mixed $message Message content to log
340: * @param string|array $context Additional data to be used for logging the message.
341: * The special `scope` key can be passed to be used for further filtering of the
342: * log engines to be used. If a string or a numerically index array is passed, it
343: * will be treated as the `scope` key.
344: * See Cake\Log\Log::setConfig() for more information on logging scopes.
345: * @return bool Success
346: * @throws \InvalidArgumentException If invalid level is passed.
347: */
348: public static function write($level, $message, $context = [])
349: {
350: static::_init();
351: if (is_int($level) && in_array($level, static::$_levelMap)) {
352: $level = array_search($level, static::$_levelMap);
353: }
354:
355: if (!in_array($level, static::$_levels)) {
356: throw new InvalidArgumentException(sprintf('Invalid log level "%s"', $level));
357: }
358:
359: $logged = false;
360: $context = (array)$context;
361: if (isset($context[0])) {
362: $context = ['scope' => $context];
363: }
364: $context += ['scope' => []];
365:
366: foreach (static::$_registry->loaded() as $streamName) {
367: $logger = static::$_registry->{$streamName};
368: $levels = $scopes = null;
369:
370: if ($logger instanceof BaseLog) {
371: $levels = $logger->levels();
372: $scopes = $logger->scopes();
373: }
374: if ($scopes === null) {
375: $scopes = [];
376: }
377:
378: $correctLevel = empty($levels) || in_array($level, $levels);
379: $inScope = $scopes === false && empty($context['scope']) || $scopes === [] ||
380: is_array($scopes) && array_intersect((array)$context['scope'], $scopes);
381:
382: if ($correctLevel && $inScope) {
383: $logger->log($level, $message, $context);
384: $logged = true;
385: }
386: }
387:
388: return $logged;
389: }
390:
391: /**
392: * Convenience method to log emergency messages
393: *
394: * @param string $message log message
395: * @param string|array $context Additional data to be used for logging the message.
396: * The special `scope` key can be passed to be used for further filtering of the
397: * log engines to be used. If a string or a numerically index array is passed, it
398: * will be treated as the `scope` key.
399: * See Cake\Log\Log::setConfig() for more information on logging scopes.
400: * @return bool Success
401: */
402: public static function emergency($message, $context = [])
403: {
404: return static::write(__FUNCTION__, $message, $context);
405: }
406:
407: /**
408: * Convenience method to log alert messages
409: *
410: * @param string $message log message
411: * @param string|array $context Additional data to be used for logging the message.
412: * The special `scope` key can be passed to be used for further filtering of the
413: * log engines to be used. If a string or a numerically index array is passed, it
414: * will be treated as the `scope` key.
415: * See Cake\Log\Log::setConfig() for more information on logging scopes.
416: * @return bool Success
417: */
418: public static function alert($message, $context = [])
419: {
420: return static::write(__FUNCTION__, $message, $context);
421: }
422:
423: /**
424: * Convenience method to log critical messages
425: *
426: * @param string $message log message
427: * @param string|array $context Additional data to be used for logging the message.
428: * The special `scope` key can be passed to be used for further filtering of the
429: * log engines to be used. If a string or a numerically index array is passed, it
430: * will be treated as the `scope` key.
431: * See Cake\Log\Log::setConfig() for more information on logging scopes.
432: * @return bool Success
433: */
434: public static function critical($message, $context = [])
435: {
436: return static::write(__FUNCTION__, $message, $context);
437: }
438:
439: /**
440: * Convenience method to log error messages
441: *
442: * @param string $message log message
443: * @param string|array $context Additional data to be used for logging the message.
444: * The special `scope` key can be passed to be used for further filtering of the
445: * log engines to be used. If a string or a numerically index array is passed, it
446: * will be treated as the `scope` key.
447: * See Cake\Log\Log::setConfig() for more information on logging scopes.
448: * @return bool Success
449: */
450: public static function error($message, $context = [])
451: {
452: return static::write(__FUNCTION__, $message, $context);
453: }
454:
455: /**
456: * Convenience method to log warning messages
457: *
458: * @param string $message log message
459: * @param string|array $context Additional data to be used for logging the message.
460: * The special `scope` key can be passed to be used for further filtering of the
461: * log engines to be used. If a string or a numerically index array is passed, it
462: * will be treated as the `scope` key.
463: * See Cake\Log\Log::setConfig() for more information on logging scopes.
464: * @return bool Success
465: */
466: public static function warning($message, $context = [])
467: {
468: return static::write(__FUNCTION__, $message, $context);
469: }
470:
471: /**
472: * Convenience method to log notice messages
473: *
474: * @param string $message log message
475: * @param string|array $context Additional data to be used for logging the message.
476: * The special `scope` key can be passed to be used for further filtering of the
477: * log engines to be used. If a string or a numerically index array is passed, it
478: * will be treated as the `scope` key.
479: * See Cake\Log\Log::setConfig() for more information on logging scopes.
480: * @return bool Success
481: */
482: public static function notice($message, $context = [])
483: {
484: return static::write(__FUNCTION__, $message, $context);
485: }
486:
487: /**
488: * Convenience method to log debug messages
489: *
490: * @param string $message log message
491: * @param string|array $context Additional data to be used for logging the message.
492: * The special `scope` key can be passed to be used for further filtering of the
493: * log engines to be used. If a string or a numerically index array is passed, it
494: * will be treated as the `scope` key.
495: * See Cake\Log\Log::setConfig() for more information on logging scopes.
496: * @return bool Success
497: */
498: public static function debug($message, $context = [])
499: {
500: return static::write(__FUNCTION__, $message, $context);
501: }
502:
503: /**
504: * Convenience method to log info messages
505: *
506: * @param string $message log message
507: * @param string|array $context Additional data to be used for logging the message.
508: * The special `scope` key can be passed to be used for further filtering of the
509: * log engines to be used. If a string or a numerically index array is passed, it
510: * will be treated as the `scope` key.
511: * See Cake\Log\Log::setConfig() for more information on logging scopes.
512: * @return bool Success
513: */
514: public static function info($message, $context = [])
515: {
516: return static::write(__FUNCTION__, $message, $context);
517: }
518: }
519: