Logger.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. <?php
  2. /*
  3. * This file is part of the Monolog package.
  4. *
  5. * (c) Jordi Boggiano <j.boggiano@seld.be>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Monolog;
  11. use Monolog\Handler\HandlerInterface;
  12. use Monolog\Handler\StreamHandler;
  13. use Psr\Log\LoggerInterface;
  14. use Psr\Log\InvalidArgumentException;
  15. /**
  16. * Monolog log channel
  17. *
  18. * It contains a stack of Handlers and a stack of Processors,
  19. * and uses them to store records that are added to it.
  20. *
  21. * @author Jordi Boggiano <j.boggiano@seld.be>
  22. */
  23. class Logger implements LoggerInterface
  24. {
  25. /**
  26. * Detailed debug information
  27. */
  28. const DEBUG = 100;
  29. /**
  30. * Interesting events
  31. *
  32. * Examples: User logs in, SQL logs.
  33. */
  34. const INFO = 200;
  35. /**
  36. * Uncommon events
  37. */
  38. const NOTICE = 250;
  39. /**
  40. * Exceptional occurrences that are not errors
  41. *
  42. * Examples: Use of deprecated APIs, poor use of an API,
  43. * undesirable things that are not necessarily wrong.
  44. */
  45. const WARNING = 300;
  46. /**
  47. * Runtime errors
  48. */
  49. const ERROR = 400;
  50. /**
  51. * Critical conditions
  52. *
  53. * Example: Application component unavailable, unexpected exception.
  54. */
  55. const CRITICAL = 500;
  56. /**
  57. * Action must be taken immediately
  58. *
  59. * Example: Entire website down, database unavailable, etc.
  60. * This should trigger the SMS alerts and wake you up.
  61. */
  62. const ALERT = 550;
  63. /**
  64. * Urgent alert.
  65. */
  66. const EMERGENCY = 600;
  67. /**
  68. * Monolog API version
  69. *
  70. * This is only bumped when API breaks are done and should
  71. * follow the major version of the library
  72. *
  73. * @var int
  74. */
  75. const API = 1;
  76. /**
  77. * Logging levels from syslog protocol defined in RFC 5424
  78. *
  79. * @var array $levels Logging levels
  80. */
  81. protected static $levels = array(
  82. 100 => 'DEBUG',
  83. 200 => 'INFO',
  84. 250 => 'NOTICE',
  85. 300 => 'WARNING',
  86. 400 => 'ERROR',
  87. 500 => 'CRITICAL',
  88. 550 => 'ALERT',
  89. 600 => 'EMERGENCY',
  90. );
  91. /**
  92. * @var \DateTimeZone
  93. */
  94. protected static $timezone;
  95. /**
  96. * @var string
  97. */
  98. protected $name;
  99. /**
  100. * The handler stack
  101. *
  102. * @var HandlerInterface[]
  103. */
  104. protected $handlers;
  105. /**
  106. * Processors that will process all log records
  107. *
  108. * To process records of a single handler instead, add the processor on that specific handler
  109. *
  110. * @var callable[]
  111. */
  112. protected $processors;
  113. /**
  114. * @param string $name The logging channel
  115. * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc.
  116. * @param callable[] $processors Optional array of processors
  117. */
  118. public function __construct($name, array $handlers = array(), array $processors = array())
  119. {
  120. $this->name = $name;
  121. $this->handlers = $handlers;
  122. $this->processors = $processors;
  123. }
  124. /**
  125. * @return string
  126. */
  127. public function getName()
  128. {
  129. return $this->name;
  130. }
  131. /**
  132. * Pushes a handler on to the stack.
  133. *
  134. * @param HandlerInterface $handler
  135. */
  136. public function pushHandler(HandlerInterface $handler)
  137. {
  138. array_unshift($this->handlers, $handler);
  139. }
  140. /**
  141. * Pops a handler from the stack
  142. *
  143. * @return HandlerInterface
  144. */
  145. public function popHandler()
  146. {
  147. if (!$this->handlers) {
  148. throw new \LogicException('You tried to pop from an empty handler stack.');
  149. }
  150. return array_shift($this->handlers);
  151. }
  152. /**
  153. * @return HandlerInterface[]
  154. */
  155. public function getHandlers()
  156. {
  157. return $this->handlers;
  158. }
  159. /**
  160. * Adds a processor on to the stack.
  161. *
  162. * @param callable $callback
  163. */
  164. public function pushProcessor($callback)
  165. {
  166. if (!is_callable($callback)) {
  167. throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
  168. }
  169. array_unshift($this->processors, $callback);
  170. }
  171. /**
  172. * Removes the processor on top of the stack and returns it.
  173. *
  174. * @return callable
  175. */
  176. public function popProcessor()
  177. {
  178. if (!$this->processors) {
  179. throw new \LogicException('You tried to pop from an empty processor stack.');
  180. }
  181. return array_shift($this->processors);
  182. }
  183. /**
  184. * @return callable[]
  185. */
  186. public function getProcessors()
  187. {
  188. return $this->processors;
  189. }
  190. /**
  191. * Adds a log record.
  192. *
  193. * @param integer $level The logging level
  194. * @param string $message The log message
  195. * @param array $context The log context
  196. * @return Boolean Whether the record has been processed
  197. */
  198. public function addRecord($level, $message, array $context = array())
  199. {
  200. if (!$this->handlers) {
  201. $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG));
  202. }
  203. $levelName = static::getLevelName($level);
  204. // check if any handler will handle this message so we can return early and save cycles
  205. $handlerKey = null;
  206. foreach ($this->handlers as $key => $handler) {
  207. if ($handler->isHandling(array('level' => $level))) {
  208. $handlerKey = $key;
  209. break;
  210. }
  211. }
  212. if (null === $handlerKey) {
  213. return false;
  214. }
  215. if (!static::$timezone) {
  216. static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
  217. }
  218. $record = array(
  219. 'message' => (string) $message,
  220. 'context' => $context,
  221. 'level' => $level,
  222. 'level_name' => $levelName,
  223. 'channel' => $this->name,
  224. 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone),
  225. 'extra' => array(),
  226. );
  227. foreach ($this->processors as $processor) {
  228. $record = call_user_func($processor, $record);
  229. }
  230. while (isset($this->handlers[$handlerKey]) &&
  231. false === $this->handlers[$handlerKey]->handle($record)) {
  232. $handlerKey++;
  233. }
  234. return true;
  235. }
  236. /**
  237. * Adds a log record at the DEBUG level.
  238. *
  239. * @param string $message The log message
  240. * @param array $context The log context
  241. * @return Boolean Whether the record has been processed
  242. */
  243. public function addDebug($message, array $context = array())
  244. {
  245. return $this->addRecord(static::DEBUG, $message, $context);
  246. }
  247. /**
  248. * Adds a log record at the INFO level.
  249. *
  250. * @param string $message The log message
  251. * @param array $context The log context
  252. * @return Boolean Whether the record has been processed
  253. */
  254. public function addInfo($message, array $context = array())
  255. {
  256. return $this->addRecord(static::INFO, $message, $context);
  257. }
  258. /**
  259. * Adds a log record at the NOTICE level.
  260. *
  261. * @param string $message The log message
  262. * @param array $context The log context
  263. * @return Boolean Whether the record has been processed
  264. */
  265. public function addNotice($message, array $context = array())
  266. {
  267. return $this->addRecord(static::NOTICE, $message, $context);
  268. }
  269. /**
  270. * Adds a log record at the WARNING level.
  271. *
  272. * @param string $message The log message
  273. * @param array $context The log context
  274. * @return Boolean Whether the record has been processed
  275. */
  276. public function addWarning($message, array $context = array())
  277. {
  278. return $this->addRecord(static::WARNING, $message, $context);
  279. }
  280. /**
  281. * Adds a log record at the ERROR level.
  282. *
  283. * @param string $message The log message
  284. * @param array $context The log context
  285. * @return Boolean Whether the record has been processed
  286. */
  287. public function addError($message, array $context = array())
  288. {
  289. return $this->addRecord(static::ERROR, $message, $context);
  290. }
  291. /**
  292. * Adds a log record at the CRITICAL level.
  293. *
  294. * @param string $message The log message
  295. * @param array $context The log context
  296. * @return Boolean Whether the record has been processed
  297. */
  298. public function addCritical($message, array $context = array())
  299. {
  300. return $this->addRecord(static::CRITICAL, $message, $context);
  301. }
  302. /**
  303. * Adds a log record at the ALERT level.
  304. *
  305. * @param string $message The log message
  306. * @param array $context The log context
  307. * @return Boolean Whether the record has been processed
  308. */
  309. public function addAlert($message, array $context = array())
  310. {
  311. return $this->addRecord(static::ALERT, $message, $context);
  312. }
  313. /**
  314. * Adds a log record at the EMERGENCY level.
  315. *
  316. * @param string $message The log message
  317. * @param array $context The log context
  318. * @return Boolean Whether the record has been processed
  319. */
  320. public function addEmergency($message, array $context = array())
  321. {
  322. return $this->addRecord(static::EMERGENCY, $message, $context);
  323. }
  324. /**
  325. * Gets all supported logging levels.
  326. *
  327. * @return array Assoc array with human-readable level names => level codes.
  328. */
  329. public static function getLevels()
  330. {
  331. return array_flip(static::$levels);
  332. }
  333. /**
  334. * Gets the name of the logging level.
  335. *
  336. * @param integer $level
  337. * @return string
  338. */
  339. public static function getLevelName($level)
  340. {
  341. if (!isset(static::$levels[$level])) {
  342. throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
  343. }
  344. return static::$levels[$level];
  345. }
  346. /**
  347. * Converts PSR-3 levels to Monolog ones if necessary
  348. *
  349. * @param string|int Level number (monolog) or name (PSR-3)
  350. * @return int
  351. */
  352. public static function toMonologLevel($level)
  353. {
  354. if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) {
  355. return constant(__CLASS__.'::'.strtoupper($level));
  356. }
  357. return $level;
  358. }
  359. /**
  360. * Checks whether the Logger has a handler that listens on the given level
  361. *
  362. * @param integer $level
  363. * @return Boolean
  364. */
  365. public function isHandling($level)
  366. {
  367. $record = array(
  368. 'level' => $level,
  369. );
  370. foreach ($this->handlers as $handler) {
  371. if ($handler->isHandling($record)) {
  372. return true;
  373. }
  374. }
  375. return false;
  376. }
  377. /**
  378. * Adds a log record at an arbitrary level.
  379. *
  380. * This method allows for compatibility with common interfaces.
  381. *
  382. * @param mixed $level The log level
  383. * @param string $message The log message
  384. * @param array $context The log context
  385. * @return Boolean Whether the record has been processed
  386. */
  387. public function log($level, $message, array $context = array())
  388. {
  389. $level = static::toMonologLevel($level);
  390. return $this->addRecord($level, $message, $context);
  391. }
  392. /**
  393. * Adds a log record at the DEBUG level.
  394. *
  395. * This method allows for compatibility with common interfaces.
  396. *
  397. * @param string $message The log message
  398. * @param array $context The log context
  399. * @return Boolean Whether the record has been processed
  400. */
  401. public function debug($message, array $context = array())
  402. {
  403. return $this->addRecord(static::DEBUG, $message, $context);
  404. }
  405. /**
  406. * Adds a log record at the INFO level.
  407. *
  408. * This method allows for compatibility with common interfaces.
  409. *
  410. * @param string $message The log message
  411. * @param array $context The log context
  412. * @return Boolean Whether the record has been processed
  413. */
  414. public function info($message, array $context = array())
  415. {
  416. return $this->addRecord(static::INFO, $message, $context);
  417. }
  418. /**
  419. * Adds a log record at the NOTICE level.
  420. *
  421. * This method allows for compatibility with common interfaces.
  422. *
  423. * @param string $message The log message
  424. * @param array $context The log context
  425. * @return Boolean Whether the record has been processed
  426. */
  427. public function notice($message, array $context = array())
  428. {
  429. return $this->addRecord(static::NOTICE, $message, $context);
  430. }
  431. /**
  432. * Adds a log record at the WARNING level.
  433. *
  434. * This method allows for compatibility with common interfaces.
  435. *
  436. * @param string $message The log message
  437. * @param array $context The log context
  438. * @return Boolean Whether the record has been processed
  439. */
  440. public function warn($message, array $context = array())
  441. {
  442. return $this->addRecord(static::WARNING, $message, $context);
  443. }
  444. /**
  445. * Adds a log record at the WARNING level.
  446. *
  447. * This method allows for compatibility with common interfaces.
  448. *
  449. * @param string $message The log message
  450. * @param array $context The log context
  451. * @return Boolean Whether the record has been processed
  452. */
  453. public function warning($message, array $context = array())
  454. {
  455. return $this->addRecord(static::WARNING, $message, $context);
  456. }
  457. /**
  458. * Adds a log record at the ERROR level.
  459. *
  460. * This method allows for compatibility with common interfaces.
  461. *
  462. * @param string $message The log message
  463. * @param array $context The log context
  464. * @return Boolean Whether the record has been processed
  465. */
  466. public function err($message, array $context = array())
  467. {
  468. return $this->addRecord(static::ERROR, $message, $context);
  469. }
  470. /**
  471. * Adds a log record at the ERROR level.
  472. *
  473. * This method allows for compatibility with common interfaces.
  474. *
  475. * @param string $message The log message
  476. * @param array $context The log context
  477. * @return Boolean Whether the record has been processed
  478. */
  479. public function error($message, array $context = array())
  480. {
  481. return $this->addRecord(static::ERROR, $message, $context);
  482. }
  483. /**
  484. * Adds a log record at the CRITICAL level.
  485. *
  486. * This method allows for compatibility with common interfaces.
  487. *
  488. * @param string $message The log message
  489. * @param array $context The log context
  490. * @return Boolean Whether the record has been processed
  491. */
  492. public function crit($message, array $context = array())
  493. {
  494. return $this->addRecord(static::CRITICAL, $message, $context);
  495. }
  496. /**
  497. * Adds a log record at the CRITICAL level.
  498. *
  499. * This method allows for compatibility with common interfaces.
  500. *
  501. * @param string $message The log message
  502. * @param array $context The log context
  503. * @return Boolean Whether the record has been processed
  504. */
  505. public function critical($message, array $context = array())
  506. {
  507. return $this->addRecord(static::CRITICAL, $message, $context);
  508. }
  509. /**
  510. * Adds a log record at the ALERT level.
  511. *
  512. * This method allows for compatibility with common interfaces.
  513. *
  514. * @param string $message The log message
  515. * @param array $context The log context
  516. * @return Boolean Whether the record has been processed
  517. */
  518. public function alert($message, array $context = array())
  519. {
  520. return $this->addRecord(static::ALERT, $message, $context);
  521. }
  522. /**
  523. * Adds a log record at the EMERGENCY level.
  524. *
  525. * This method allows for compatibility with common interfaces.
  526. *
  527. * @param string $message The log message
  528. * @param array $context The log context
  529. * @return Boolean Whether the record has been processed
  530. */
  531. public function emerg($message, array $context = array())
  532. {
  533. return $this->addRecord(static::EMERGENCY, $message, $context);
  534. }
  535. /**
  536. * Adds a log record at the EMERGENCY level.
  537. *
  538. * This method allows for compatibility with common interfaces.
  539. *
  540. * @param string $message The log message
  541. * @param array $context The log context
  542. * @return Boolean Whether the record has been processed
  543. */
  544. public function emergency($message, array $context = array())
  545. {
  546. return $this->addRecord(static::EMERGENCY, $message, $context);
  547. }
  548. }