PageRenderTime 44ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Log/Log.php

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