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:
107: use StaticConfigTrait {
108: setConfig as protected _setConfig;
109: }
110:
111: /**
112: * An array mapping url schemes to fully qualified Log engine class names
113: *
114: * @var array
115: */
116: protected static $_dsnClassMap = [
117: 'console' => 'Cake\Log\Engine\ConsoleLog',
118: 'file' => 'Cake\Log\Engine\FileLog',
119: 'syslog' => 'Cake\Log\Engine\SyslogLog',
120: ];
121:
122: /**
123: * Internal flag for tracking whether or not configuration has been changed.
124: *
125: * @var bool
126: */
127: protected static $_dirtyConfig = false;
128:
129: /**
130: * LogEngineRegistry class
131: *
132: * @var \Cake\Log\LogEngineRegistry|null
133: */
134: protected static $_registry;
135:
136: /**
137: * Handled log levels
138: *
139: * @var array
140: */
141: protected static $_levels = [
142: 'emergency',
143: 'alert',
144: 'critical',
145: 'error',
146: 'warning',
147: 'notice',
148: 'info',
149: 'debug'
150: ];
151:
152: /**
153: * Log levels as detailed in RFC 5424
154: * https://tools.ietf.org/html/rfc5424
155: *
156: * @var array
157: */
158: protected static $_levelMap = [
159: 'emergency' => LOG_EMERG,
160: 'alert' => LOG_ALERT,
161: 'critical' => LOG_CRIT,
162: 'error' => LOG_ERR,
163: 'warning' => LOG_WARNING,
164: 'notice' => LOG_NOTICE,
165: 'info' => LOG_INFO,
166: 'debug' => LOG_DEBUG,
167: ];
168:
169: /**
170: * Initializes registry and configurations
171: *
172: * @return void
173: */
174: protected static function _init()
175: {
176: if (empty(static::$_registry)) {
177: static::$_registry = new LogEngineRegistry();
178: }
179: if (static::$_dirtyConfig) {
180: static::_loadConfig();
181: }
182: static::$_dirtyConfig = false;
183: }
184:
185: /**
186: * Load the defined configuration and create all the defined logging
187: * adapters.
188: *
189: * @return void
190: */
191: protected static function _loadConfig()
192: {
193: foreach (static::$_config as $name => $properties) {
194: if (isset($properties['engine'])) {
195: $properties['className'] = $properties['engine'];
196: }
197: if (!static::$_registry->has($name)) {
198: static::$_registry->load($name, $properties);
199: }
200: }
201: }
202:
203: /**
204: * Reset all the connected loggers. This is useful to do when changing the logging
205: * configuration or during testing when you want to reset the internal state of the
206: * Log class.
207: *
208: * Resets the configured logging adapters, as well as any custom logging levels.
209: * This will also clear the configuration data.
210: *
211: * @return void
212: */
213: public static function reset()
214: {
215: static::$_registry = null;
216: static::$_config = [];
217: static::$_dirtyConfig = true;
218: }
219:
220: /**
221: * Gets log levels
222: *
223: * Call this method to obtain current
224: * level configuration.
225: *
226: * @return array active log levels
227: */
228: public static function levels()
229: {
230: return static::$_levels;
231: }
232:
233: /**
234: * This method can be used to define logging adapters for an application
235: * or read existing configuration.
236: *
237: * To change an adapter's configuration at runtime, first drop the adapter and then
238: * reconfigure it.
239: *
240: * Loggers will not be constructed until the first log message is written.
241: *
242: * ### Usage
243: *
244: * Setting a cache engine up.
245: *
246: * ```
247: * Log::setConfig('default', $settings);
248: * ```
249: *
250: * Injecting a constructed adapter in:
251: *
252: * ```
253: * Log::setConfig('default', $instance);
254: * ```
255: *
256: * Using a factory function to get an adapter:
257: *
258: * ```
259: * Log::setConfig('default', function () { return new FileLog(); });
260: * ```
261: *
262: * Configure multiple adapters at once:
263: *
264: * ```
265: * Log::setConfig($arrayOfConfig);
266: * ```
267: *
268: * @param string|array $key The name of the logger config, or an array of multiple configs.
269: * @param array|null $config An array of name => config data for adapter.
270: * @return void
271: * @throws \BadMethodCallException When trying to modify an existing config.
272: */
273: public static function setConfig($key, $config = null)
274: {
275: static::_setConfig($key, $config);
276: static::$_dirtyConfig = true;
277: }
278:
279: /**
280: * Get a logging engine.
281: *
282: * @param string $name Key name of a configured adapter to get.
283: * @return \Cake\Log\Engine\BaseLog|false Instance of BaseLog or false if not found
284: */
285: public static function engine($name)
286: {
287: static::_init();
288: if (static::$_registry->{$name}) {
289: return static::$_registry->{$name};
290: }
291:
292: return false;
293: }
294:
295: /**
296: * Writes the given message and type to all of the configured log adapters.
297: * Configured adapters are passed both the $level and $message variables. $level
298: * is one of the following strings/values.
299: *
300: * ### Levels:
301: *
302: * - `LOG_EMERG` => 'emergency',
303: * - `LOG_ALERT` => 'alert',
304: * - `LOG_CRIT` => 'critical',
305: * - `LOG_ERR` => 'error',
306: * - `LOG_WARNING` => 'warning',
307: * - `LOG_NOTICE` => 'notice',
308: * - `LOG_INFO` => 'info',
309: * - `LOG_DEBUG` => 'debug',
310: *
311: * ### Basic usage
312: *
313: * Write a 'warning' message to the logs:
314: *
315: * ```
316: * Log::write('warning', 'Stuff is broken here');
317: * ```
318: *
319: * ### Using scopes
320: *
321: * When writing a log message you can define one or many scopes for the message.
322: * This allows you to handle messages differently based on application section/feature.
323: *
324: * ```
325: * Log::write('warning', 'Payment failed', ['scope' => 'payment']);
326: * ```
327: *
328: * When configuring loggers you can configure the scopes a particular logger will handle.
329: * When using scopes, you must ensure that the level of the message, and the scope of the message
330: * intersect with the defined levels & scopes for a logger.
331: *
332: * ### Unhandled log messages
333: *
334: * If no configured logger can handle a log message (because of level or scope restrictions)
335: * then the logged message will be ignored and silently dropped. You can check if this has happened
336: * by inspecting the return of write(). If false the message was not handled.
337: *
338: * @param int|string $level The severity level of the message being written.
339: * The value must be an integer or string matching a known level.
340: * @param mixed $message Message content to log
341: * @param string|array $context Additional data to be used for logging the message.
342: * The special `scope` key can be passed to be used for further filtering of the
343: * log engines to be used. If a string or a numerically index array is passed, it
344: * will be treated as the `scope` key.
345: * See Cake\Log\Log::setConfig() for more information on logging scopes.
346: * @return bool Success
347: * @throws \InvalidArgumentException If invalid level is passed.
348: */
349: public static function write($level, $message, $context = [])
350: {
351: static::_init();
352: if (is_int($level) && in_array($level, static::$_levelMap)) {
353: $level = array_search($level, static::$_levelMap);
354: }
355:
356: if (!in_array($level, static::$_levels)) {
357: throw new InvalidArgumentException(sprintf('Invalid log level "%s"', $level));
358: }
359:
360: $logged = false;
361: $context = (array)$context;
362: if (isset($context[0])) {
363: $context = ['scope' => $context];
364: }
365: $context += ['scope' => []];
366:
367: foreach (static::$_registry->loaded() as $streamName) {
368: $logger = static::$_registry->{$streamName};
369: $levels = $scopes = null;
370:
371: if ($logger instanceof BaseLog) {
372: $levels = $logger->levels();
373: $scopes = $logger->scopes();
374: }
375: if ($scopes === null) {
376: $scopes = [];
377: }
378:
379: $correctLevel = empty($levels) || in_array($level, $levels);
380: $inScope = $scopes === false && empty($context['scope']) || $scopes === [] ||
381: is_array($scopes) && array_intersect((array)$context['scope'], $scopes);
382:
383: if ($correctLevel && $inScope) {
384: $logger->log($level, $message, $context);
385: $logged = true;
386: }
387: }
388:
389: return $logged;
390: }
391:
392: /**
393: * Convenience method to log emergency messages
394: *
395: * @param string $message log message
396: * @param string|array $context Additional data to be used for logging the message.
397: * The special `scope` key can be passed to be used for further filtering of the
398: * log engines to be used. If a string or a numerically index array is passed, it
399: * will be treated as the `scope` key.
400: * See Cake\Log\Log::setConfig() for more information on logging scopes.
401: * @return bool Success
402: */
403: public static function emergency($message, $context = [])
404: {
405: return static::write(__FUNCTION__, $message, $context);
406: }
407:
408: /**
409: * Convenience method to log alert messages
410: *
411: * @param string $message log message
412: * @param string|array $context Additional data to be used for logging the message.
413: * The special `scope` key can be passed to be used for further filtering of the
414: * log engines to be used. If a string or a numerically index array is passed, it
415: * will be treated as the `scope` key.
416: * See Cake\Log\Log::setConfig() for more information on logging scopes.
417: * @return bool Success
418: */
419: public static function alert($message, $context = [])
420: {
421: return static::write(__FUNCTION__, $message, $context);
422: }
423:
424: /**
425: * Convenience method to log critical messages
426: *
427: * @param string $message log message
428: * @param string|array $context Additional data to be used for logging the message.
429: * The special `scope` key can be passed to be used for further filtering of the
430: * log engines to be used. If a string or a numerically index array is passed, it
431: * will be treated as the `scope` key.
432: * See Cake\Log\Log::setConfig() for more information on logging scopes.
433: * @return bool Success
434: */
435: public static function critical($message, $context = [])
436: {
437: return static::write(__FUNCTION__, $message, $context);
438: }
439:
440: /**
441: * Convenience method to log error messages
442: *
443: * @param string $message log message
444: * @param string|array $context Additional data to be used for logging the message.
445: * The special `scope` key can be passed to be used for further filtering of the
446: * log engines to be used. If a string or a numerically index array is passed, it
447: * will be treated as the `scope` key.
448: * See Cake\Log\Log::setConfig() for more information on logging scopes.
449: * @return bool Success
450: */
451: public static function error($message, $context = [])
452: {
453: return static::write(__FUNCTION__, $message, $context);
454: }
455:
456: /**
457: * Convenience method to log warning messages
458: *
459: * @param string $message log message
460: * @param string|array $context Additional data to be used for logging the message.
461: * The special `scope` key can be passed to be used for further filtering of the
462: * log engines to be used. If a string or a numerically index array is passed, it
463: * will be treated as the `scope` key.
464: * See Cake\Log\Log::setConfig() for more information on logging scopes.
465: * @return bool Success
466: */
467: public static function warning($message, $context = [])
468: {
469: return static::write(__FUNCTION__, $message, $context);
470: }
471:
472: /**
473: * Convenience method to log notice messages
474: *
475: * @param string $message log message
476: * @param string|array $context Additional data to be used for logging the message.
477: * The special `scope` key can be passed to be used for further filtering of the
478: * log engines to be used. If a string or a numerically index array is passed, it
479: * will be treated as the `scope` key.
480: * See Cake\Log\Log::setConfig() for more information on logging scopes.
481: * @return bool Success
482: */
483: public static function notice($message, $context = [])
484: {
485: return static::write(__FUNCTION__, $message, $context);
486: }
487:
488: /**
489: * Convenience method to log debug messages
490: *
491: * @param string $message log message
492: * @param string|array $context Additional data to be used for logging the message.
493: * The special `scope` key can be passed to be used for further filtering of the
494: * log engines to be used. If a string or a numerically index array is passed, it
495: * will be treated as the `scope` key.
496: * See Cake\Log\Log::setConfig() for more information on logging scopes.
497: * @return bool Success
498: */
499: public static function debug($message, $context = [])
500: {
501: return static::write(__FUNCTION__, $message, $context);
502: }
503:
504: /**
505: * Convenience method to log info messages
506: *
507: * @param string $message log message
508: * @param string|array $context Additional data to be used for logging the message.
509: * The special `scope` key can be passed to be used for further filtering of the
510: * log engines to be used. If a string or a numerically index array is passed, it
511: * will be treated as the `scope` key.
512: * See Cake\Log\Log::setConfig() for more information on logging scopes.
513: * @return bool Success
514: */
515: public static function info($message, $context = [])
516: {
517: return static::write(__FUNCTION__, $message, $context);
518: }
519: }
520: