Log::write()   F
last analyzed

Complexity

Conditions 15
Paths 454

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
nc 454
nop 3
dl 0
loc 42
rs 2.5083
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 string[]
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 string[]
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 string[] 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