1<?php 2/** 3 * Logging. 4 * 5 * Log messages to text files. 6 * 7 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org) 8 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 9 * 10 * Licensed under The MIT License 11 * For full copyright and license information, please see the LICENSE.txt 12 * Redistributions of files must retain the above copyright notice. 13 * 14 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) 15 * @link https://cakephp.org CakePHP(tm) Project 16 * @package Cake.Log 17 * @since CakePHP(tm) v 0.2.9 18 * @license https://opensource.org/licenses/mit-license.php MIT License 19 */ 20 21App::uses('LogEngineCollection', 'Log'); 22 23/** 24 * Logs messages to configured Log adapters. 25 * 26 * One or more adapters 27 * can be configured using CakeLogs's methods. 28 * 29 * ### Configuring Log adapters 30 * 31 * You can configure log adapters in your applications `bootstrap.php` file. 32 * A sample configuration would look like: 33 * 34 * ``` 35 * CakeLog::config('my_log', array('engine' => 'File')); 36 * ``` 37 * 38 * See the documentation on CakeLog::config() for more detail. 39 * 40 * ### Writing to the log 41 * 42 * You write to the logs using CakeLog::write(). See its documentation for more 43 * information. 44 * 45 * ### Logging Levels 46 * 47 * By default CakeLog supports all the log levels defined in 48 * RFC 5424. When logging messages you can either use the named methods, 49 * or the correct constants with `write()`: 50 * 51 * ``` 52 * CakeLog::error('Something horrible happened'); 53 * CakeLog::write(LOG_ERR, 'Something horrible happened'); 54 * ``` 55 * 56 * If you require custom logging levels, you can use CakeLog::levels() to 57 * append additional logging levels. 58 * 59 * ### Logging scopes 60 * 61 * When logging messages and configuring log adapters, you can specify 62 * 'scopes' that the logger will handle. You can think of scopes as subsystems 63 * in your application that may require different logging setups. For 64 * example in an e-commerce application you may want to handle logged errors 65 * in the cart and ordering subsystems differently than the rest of the 66 * application. By using scopes you can control logging for each part 67 * of your application and still keep standard log levels. 68 * 69 * See CakeLog::config() and CakeLog::write() for more information 70 * on scopes 71 * 72 * @package Cake.Log 73 * @link https://book.cakephp.org/2.0/en/core-libraries/logging.html#logging 74 */ 75class CakeLog { 76 77/** 78 * LogEngineCollection class 79 * 80 * @var LogEngineCollection 81 */ 82 protected static $_Collection; 83 84/** 85 * Default log levels as detailed in RFC 5424 86 * http://tools.ietf.org/html/rfc5424 87 * 88 * Windows has fewer levels, thus notice, info and debug are the same. 89 * https://bugs.php.net/bug.php?id=18090 90 * 91 * @var array 92 */ 93 protected static $_defaultLevels = array( 94 'emergency' => LOG_EMERG, 95 'alert' => LOG_ALERT, 96 'critical' => LOG_CRIT, 97 'error' => LOG_ERR, 98 'warning' => LOG_WARNING, 99 'notice' => LOG_NOTICE, 100 'info' => LOG_INFO, 101 'debug' => LOG_DEBUG, 102 ); 103 104/** 105 * Active log levels for this instance. 106 * 107 * @var array 108 */ 109 protected static $_levels; 110 111/** 112 * Mapped log levels 113 * 114 * @var array 115 */ 116 protected static $_levelMap; 117 118/** 119 * initialize ObjectCollection 120 * 121 * @return void 122 */ 123 protected static function _init() { 124 static::$_levels = static::defaultLevels(); 125 static::$_Collection = new LogEngineCollection(); 126 } 127 128/** 129 * Configure and add a new logging stream to CakeLog 130 * You can use add loggers from app/Log/Engine use app.loggername, or any 131 * plugin/Log/Engine using plugin.loggername. 132 * 133 * ### Usage: 134 * 135 * ``` 136 * CakeLog::config('second_file', array( 137 * 'engine' => 'File', 138 * 'path' => '/var/logs/my_app/' 139 * )); 140 * ``` 141 * 142 * Will configure a FileLog instance to use the specified path. 143 * All options that are not `engine` are passed onto the logging adapter, 144 * and handled there. Any class can be configured as a logging 145 * adapter as long as it implements the methods in CakeLogInterface. 146 * 147 * ### Logging levels 148 * 149 * When configuring loggers, you can set which levels a logger will handle. 150 * This allows you to disable debug messages in production for example: 151 * 152 * ``` 153 * CakeLog::config('default', array( 154 * 'engine' => 'File', 155 * 'path' => LOGS, 156 * 'levels' => array('error', 'critical', 'alert', 'emergency') 157 * )); 158 * ``` 159 * 160 * The above logger would only log error messages or higher. Any 161 * other log messages would be discarded. 162 * 163 * ### Logging scopes 164 * 165 * When configuring loggers you can define the active scopes the logger 166 * is for. If defined only the listed scopes will be handled by the 167 * logger. If you don't define any scopes an adapter will catch 168 * all scopes that match the handled levels. 169 * 170 * ``` 171 * CakeLog::config('payments', array( 172 * 'engine' => 'File', 173 * 'types' => array('info', 'error', 'warning'), 174 * 'scopes' => array('payment', 'order') 175 * )); 176 * ``` 177 * 178 * The above logger will only capture log entries made in the 179 * `payment` and `order` scopes. All other scopes including the 180 * undefined scope will be ignored. Its important to remember that 181 * when using scopes you must also define the `types` of log messages 182 * that a logger will handle. Failing to do so will result in the logger 183 * catching all log messages even if the scope is incorrect. 184 * 185 * @param string $key The keyname for this logger, used to remove the 186 * logger later. 187 * @param array $config Array of configuration information for the logger 188 * @return bool success of configuration. 189 * @throws CakeLogException 190 * @link https://book.cakephp.org/2.0/en/core-libraries/logging.html#creating-and-configuring-log-streams 191 */ 192 public static function config($key, $config) { 193 if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $key)) { 194 throw new CakeLogException(__d('cake_dev', 'Invalid key name')); 195 } 196 if (empty($config['engine'])) { 197 throw new CakeLogException(__d('cake_dev', 'Missing logger class name')); 198 } 199 if (empty(static::$_Collection)) { 200 static::_init(); 201 } 202 static::$_Collection->load($key, $config); 203 return true; 204 } 205 206/** 207 * Returns the keynames of the currently active streams 208 * 209 * @return array Array of configured log streams. 210 */ 211 public static function configured() { 212 if (empty(static::$_Collection)) { 213 static::_init(); 214 } 215 return static::$_Collection->loaded(); 216 } 217 218/** 219 * Gets/sets log levels 220 * 221 * Call this method without arguments, eg: `CakeLog::levels()` to obtain current 222 * level configuration. 223 * 224 * To append additional level 'user0' and 'user1' to to default log levels: 225 * 226 * ``` 227 * CakeLog::levels(array('user0, 'user1')); 228 * // or 229 * CakeLog::levels(array('user0, 'user1'), true); 230 * ``` 231 * 232 * will result in: 233 * 234 * ``` 235 * array( 236 * 0 => 'emergency', 237 * 1 => 'alert', 238 * ... 239 * 8 => 'user0', 240 * 9 => 'user1', 241 * ); 242 * ``` 243 * 244 * To set/replace existing configuration, pass an array with the second argument 245 * set to false. 246 * 247 * ``` 248 * CakeLog::levels(array('user0, 'user1'), false); 249 * ``` 250 * 251 * will result in: 252 * 253 * ``` 254 * array( 255 * 0 => 'user0', 256 * 1 => 'user1', 257 * ); 258 * ``` 259 * 260 * @param array $levels array 261 * @param bool $append true to append, false to replace 262 * @return array Active log levels 263 */ 264 public static function levels($levels = array(), $append = true) { 265 if (empty(static::$_Collection)) { 266 static::_init(); 267 } 268 if (empty($levels)) { 269 return static::$_levels; 270 } 271 $levels = array_values($levels); 272 if ($append) { 273 static::$_levels = array_merge(static::$_levels, $levels); 274 } else { 275 static::$_levels = $levels; 276 } 277 static::$_levelMap = array_flip(static::$_levels); 278 return static::$_levels; 279 } 280 281/** 282 * Reset log levels to the original value 283 * 284 * @return array Default log levels 285 */ 286 public static function defaultLevels() { 287 static::$_levelMap = static::$_defaultLevels; 288 static::$_levels = array_flip(static::$_levelMap); 289 return static::$_levels; 290 } 291 292/** 293 * Removes a stream from the active streams. Once a stream has been removed 294 * it will no longer have messages sent to it. 295 * 296 * @param string $streamName Key name of a configured stream to remove. 297 * @return void 298 */ 299 public static function drop($streamName) { 300 if (empty(static::$_Collection)) { 301 static::_init(); 302 } 303 static::$_Collection->unload($streamName); 304 } 305 306/** 307 * Checks whether $streamName is enabled 308 * 309 * @param string $streamName to check 310 * @return bool 311 * @throws CakeLogException 312 */ 313 public static function enabled($streamName) { 314 if (empty(static::$_Collection)) { 315 static::_init(); 316 } 317 if (!isset(static::$_Collection->{$streamName})) { 318 throw new CakeLogException(__d('cake_dev', 'Stream %s not found', $streamName)); 319 } 320 return static::$_Collection->enabled($streamName); 321 } 322 323/** 324 * Enable stream. Streams that were previously disabled 325 * can be re-enabled with this method. 326 * 327 * @param string $streamName to enable 328 * @return void 329 * @throws CakeLogException 330 */ 331 public static function enable($streamName) { 332 if (empty(static::$_Collection)) { 333 static::_init(); 334 } 335 if (!isset(static::$_Collection->{$streamName})) { 336 throw new CakeLogException(__d('cake_dev', 'Stream %s not found', $streamName)); 337 } 338 static::$_Collection->enable($streamName); 339 } 340 341/** 342 * Disable stream. Disabling a stream will 343 * prevent that log stream from receiving any messages until 344 * its re-enabled. 345 * 346 * @param string $streamName to disable 347 * @return void 348 * @throws CakeLogException 349 */ 350 public static function disable($streamName) { 351 if (empty(static::$_Collection)) { 352 static::_init(); 353 } 354 if (!isset(static::$_Collection->{$streamName})) { 355 throw new CakeLogException(__d('cake_dev', 'Stream %s not found', $streamName)); 356 } 357 static::$_Collection->disable($streamName); 358 } 359 360/** 361 * Gets the logging engine from the active streams. 362 * 363 * @param string $streamName Key name of a configured stream to get. 364 * @return mixed instance of BaseLog or false if not found 365 * @see BaseLog 366 */ 367 public static function stream($streamName) { 368 if (empty(static::$_Collection)) { 369 static::_init(); 370 } 371 if (!empty(static::$_Collection->{$streamName})) { 372 return static::$_Collection->{$streamName}; 373 } 374 return false; 375 } 376 377/** 378 * Writes the given message and type to all of the configured log adapters. 379 * Configured adapters are passed both the $type and $message variables. $type 380 * is one of the following strings/values. 381 * 382 * ### Types: 383 * 384 * - LOG_EMERG => 'emergency', 385 * - LOG_ALERT => 'alert', 386 * - LOG_CRIT => 'critical', 387 * - `LOG_ERR` => 'error', 388 * - `LOG_WARNING` => 'warning', 389 * - `LOG_NOTICE` => 'notice', 390 * - `LOG_INFO` => 'info', 391 * - `LOG_DEBUG` => 'debug', 392 * 393 * ### Usage: 394 * 395 * Write a message to the 'warning' log: 396 * 397 * `CakeLog::write('warning', 'Stuff is broken here');` 398 * 399 * @param int|string $type Type of message being written. When value is an integer 400 * or a string matching the recognized levels, then it will 401 * be treated as a log level. Otherwise it's treated as a scope. 402 * @param string $message Message content to log 403 * @param string|array $scope The scope(s) a log message is being created in. 404 * See CakeLog::config() for more information on logging scopes. 405 * @return bool Success 406 * @link https://book.cakephp.org/2.0/en/core-libraries/logging.html#writing-to-logs 407 */ 408 public static function write($type, $message, $scope = array()) { 409 if (empty(static::$_Collection)) { 410 static::_init(); 411 } 412 413 if (is_int($type) && isset(static::$_levels[$type])) { 414 $type = static::$_levels[$type]; 415 } 416 if (is_string($type) && empty($scope) && !in_array($type, static::$_levels)) { 417 $scope = $type; 418 } 419 $logged = false; 420 foreach (static::$_Collection->enabled() as $streamName) { 421 $logger = static::$_Collection->{$streamName}; 422 $types = $scopes = $config = array(); 423 if (method_exists($logger, 'config')) { 424 $config = $logger->config(); 425 } 426 if (isset($config['types'])) { 427 $types = $config['types']; 428 } 429 if (isset($config['scopes'])) { 430 $scopes = $config['scopes']; 431 } 432 $inScope = (count(array_intersect((array)$scope, $scopes)) > 0); 433 $correctLevel = in_array($type, $types); 434 435 if ( 436 // No config is a catch all (bc mode) 437 (empty($types) && empty($scopes)) || 438 // BC layer for mixing scope & level 439 (in_array($type, $scopes)) || 440 // no scopes, but has level 441 (empty($scopes) && $correctLevel) || 442 // exact scope + level 443 ($correctLevel && $inScope) 444 ) { 445 $logger->write($type, $message); 446 $logged = true; 447 } 448 } 449 return $logged; 450 } 451 452/** 453 * Convenience method to log emergency messages 454 * 455 * @param string $message log message 456 * @param string|array $scope The scope(s) a log message is being created in. 457 * See CakeLog::config() for more information on logging scopes. 458 * @return bool Success 459 */ 460 public static function emergency($message, $scope = array()) { 461 return static::write(static::$_levelMap['emergency'], $message, $scope); 462 } 463 464/** 465 * Convenience method to log alert messages 466 * 467 * @param string $message log message 468 * @param string|array $scope The scope(s) a log message is being created in. 469 * See CakeLog::config() for more information on logging scopes. 470 * @return bool Success 471 */ 472 public static function alert($message, $scope = array()) { 473 return static::write(static::$_levelMap['alert'], $message, $scope); 474 } 475 476/** 477 * Convenience method to log critical messages 478 * 479 * @param string $message log message 480 * @param string|array $scope The scope(s) a log message is being created in. 481 * See CakeLog::config() for more information on logging scopes. 482 * @return bool Success 483 */ 484 public static function critical($message, $scope = array()) { 485 return static::write(static::$_levelMap['critical'], $message, $scope); 486 } 487 488/** 489 * Convenience method to log error messages 490 * 491 * @param string $message log message 492 * @param string|array $scope The scope(s) a log message is being created in. 493 * See CakeLog::config() for more information on logging scopes. 494 * @return bool Success 495 */ 496 public static function error($message, $scope = array()) { 497 return static::write(static::$_levelMap['error'], $message, $scope); 498 } 499 500/** 501 * Convenience method to log warning messages 502 * 503 * @param string $message log message 504 * @param string|array $scope The scope(s) a log message is being created in. 505 * See CakeLog::config() for more information on logging scopes. 506 * @return bool Success 507 */ 508 public static function warning($message, $scope = array()) { 509 return static::write(static::$_levelMap['warning'], $message, $scope); 510 } 511 512/** 513 * Convenience method to log notice messages 514 * 515 * @param string $message log message 516 * @param string|array $scope The scope(s) a log message is being created in. 517 * See CakeLog::config() for more information on logging scopes. 518 * @return bool Success 519 */ 520 public static function notice($message, $scope = array()) { 521 return static::write(static::$_levelMap['notice'], $message, $scope); 522 } 523 524/** 525 * Convenience method to log debug messages 526 * 527 * @param string $message log message 528 * @param string|array $scope The scope(s) a log message is being created in. 529 * See CakeLog::config() for more information on logging scopes. 530 * @return bool Success 531 */ 532 public static function debug($message, $scope = array()) { 533 return static::write(static::$_levelMap['debug'], $message, $scope); 534 } 535 536/** 537 * Convenience method to log info messages 538 * 539 * @param string $message log message 540 * @param string|array $scope The scope(s) a log message is being created in. 541 * See CakeLog::config() for more information on logging scopes. 542 * @return bool Success 543 */ 544 public static function info($message, $scope = array()) { 545 return static::write(static::$_levelMap['info'], $message, $scope); 546 } 547 548} 549