Logger.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  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. * @return $this
  136. */
  137. public function pushHandler(HandlerInterface $handler)
  138. {
  139. array_unshift($this->handlers, $handler);
  140. return $this;
  141. }
  142. /**
  143. * Pops a handler from the stack
  144. *
  145. * @return HandlerInterface
  146. */
  147. public function popHandler()
  148. {
  149. if (!$this->handlers) {
  150. throw new \LogicException('You tried to pop from an empty handler stack.');
  151. }
  152. return array_shift($this->handlers);
  153. }
  154. /**
  155. * @return HandlerInterface[]
  156. */
  157. public function getHandlers()
  158. {
  159. return $this->handlers;
  160. }
  161. /**
  162. * Adds a processor on to the stack.
  163. *
  164. * @param callable $callback
  165. * @return $this
  166. */
  167. public function pushProcessor($callback)
  168. {
  169. if (!is_callable($callback)) {
  170. throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), '.var_export($callback, true).' given');
  171. }
  172. array_unshift($this->processors, $callback);
  173. return $this;
  174. }
  175. /**
  176. * Removes the processor on top of the stack and returns it.
  177. *
  178. * @return callable
  179. */
  180. public function popProcessor()
  181. {
  182. if (!$this->processors) {
  183. throw new \LogicException('You tried to pop from an empty processor stack.');
  184. }
  185. return array_shift($this->processors);
  186. }
  187. /**
  188. * @return callable[]
  189. */
  190. public function getProcessors()
  191. {
  192. return $this->processors;
  193. }
  194. /**
  195. * Adds a log record.
  196. *
  197. * @param integer $level The logging level
  198. * @param string $message The log message
  199. * @param array $context The log context
  200. * @return Boolean Whether the record has been processed
  201. */
  202. public function addRecord($level, $message, array $context = array())
  203. {
  204. if (!$this->handlers) {
  205. $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG));
  206. }
  207. $levelName = static::getLevelName($level);
  208. // check if any handler will handle this message so we can return early and save cycles
  209. $handlerKey = null;
  210. foreach ($this->handlers as $key => $handler) {
  211. if ($handler->isHandling(array('level' => $level))) {
  212. $handlerKey = $key;
  213. break;
  214. }
  215. }
  216. if (null === $handlerKey) {
  217. return false;
  218. }
  219. if (!static::$timezone) {
  220. static::$timezone = new \DateTimeZone(date_default_timezone_get() ?: 'UTC');
  221. }
  222. $record = array(
  223. 'message' => (string) $message,
  224. 'context' => $context,
  225. 'level' => $level,
  226. 'level_name' => $levelName,
  227. 'channel' => $this->name,
  228. 'datetime' => \DateTime::createFromFormat('U.u', sprintf('%.6F', microtime(true)), static::$timezone)->setTimezone(static::$timezone),
  229. 'extra' => array(),
  230. );
  231. foreach ($this->processors as $processor) {
  232. $record = call_user_func($processor, $record);
  233. }
  234. while (isset($this->handlers[$handlerKey]) &&
  235. false === $this->handlers[$handlerKey]->handle($record)) {
  236. $handlerKey++;
  237. }
  238. return true;
  239. }
  240. /**
  241. * Adds a log record at the DEBUG level.
  242. *
  243. * @param string $message The log message
  244. * @param array $context The log context
  245. * @return Boolean Whether the record has been processed
  246. */
  247. public function addDebug($message, array $context = array())
  248. {
  249. return $this->addRecord(static::DEBUG, $message, $context);
  250. }
  251. /**
  252. * Adds a log record at the INFO level.
  253. *
  254. * @param string $message The log message
  255. * @param array $context The log context
  256. * @return Boolean Whether the record has been processed
  257. */
  258. public function addInfo($message, array $context = array())
  259. {
  260. return $this->addRecord(static::INFO, $message, $context);
  261. }
  262. /**
  263. * Adds a log record at the NOTICE level.
  264. *
  265. * @param string $message The log message
  266. * @param array $context The log context
  267. * @return Boolean Whether the record has been processed
  268. */
  269. public function addNotice($message, array $context = array())
  270. {
  271. return $this->addRecord(static::NOTICE, $message, $context);
  272. }
  273. /**
  274. * Adds a log record at the WARNING level.
  275. *
  276. * @param string $message The log message
  277. * @param array $context The log context
  278. * @return Boolean Whether the record has been processed
  279. */
  280. public function addWarning($message, array $context = array())
  281. {
  282. return $this->addRecord(static::WARNING, $message, $context);
  283. }
  284. /**
  285. * Adds a log record at the ERROR level.
  286. *
  287. * @param string $message The log message
  288. * @param array $context The log context
  289. * @return Boolean Whether the record has been processed
  290. */
  291. public function addError($message, array $context = array())
  292. {
  293. return $this->addRecord(static::ERROR, $message, $context);
  294. }
  295. /**
  296. * Adds a log record at the CRITICAL level.
  297. *
  298. * @param string $message The log message
  299. * @param array $context The log context
  300. * @return Boolean Whether the record has been processed
  301. */
  302. public function addCritical($message, array $context = array())
  303. {
  304. return $this->addRecord(static::CRITICAL, $message, $context);
  305. }
  306. /**
  307. * Adds a log record at the ALERT level.
  308. *
  309. * @param string $message The log message
  310. * @param array $context The log context
  311. * @return Boolean Whether the record has been processed
  312. */
  313. public function addAlert($message, array $context = array())
  314. {
  315. return $this->addRecord(static::ALERT, $message, $context);
  316. }
  317. /**
  318. * Adds a log record at the EMERGENCY level.
  319. *
  320. * @param string $message The log message
  321. * @param array $context The log context
  322. * @return Boolean Whether the record has been processed
  323. */
  324. public function addEmergency($message, array $context = array())
  325. {
  326. return $this->addRecord(static::EMERGENCY, $message, $context);
  327. }
  328. /**
  329. * Gets all supported logging levels.
  330. *
  331. * @return array Assoc array with human-readable level names => level codes.
  332. */
  333. public static function getLevels()
  334. {
  335. return array_flip(static::$levels);
  336. }
  337. /**
  338. * Gets the name of the logging level.
  339. *
  340. * @param integer $level
  341. * @return string
  342. */
  343. public static function getLevelName($level)
  344. {
  345. if (!isset(static::$levels[$level])) {
  346. throw new InvalidArgumentException('Level "'.$level.'" is not defined, use one of: '.implode(', ', array_keys(static::$levels)));
  347. }
  348. return static::$levels[$level];
  349. }
  350. /**
  351. * Converts PSR-3 levels to Monolog ones if necessary
  352. *
  353. * @param string|int Level number (monolog) or name (PSR-3)
  354. * @return int
  355. */
  356. public static function toMonologLevel($level)
  357. {
  358. if (is_string($level) && defined(__CLASS__.'::'.strtoupper($level))) {
  359. return constant(__CLASS__.'::'.strtoupper($level));
  360. }
  361. return $level;
  362. }
  363. /**
  364. * Checks whether the Logger has a handler that listens on the given level
  365. *
  366. * @param integer $level
  367. * @return Boolean
  368. */
  369. public function isHandling($level)
  370. {
  371. $record = array(
  372. 'level' => $level,
  373. );
  374. foreach ($this->handlers as $handler) {
  375. if ($handler->isHandling($record)) {
  376. return true;
  377. }
  378. }
  379. return false;
  380. }
  381. /**
  382. * Adds a log record at an arbitrary level.
  383. *
  384. * This method allows for compatibility with common interfaces.
  385. *
  386. * @param mixed $level The log level
  387. * @param string $message The log message
  388. * @param array $context The log context
  389. * @return Boolean Whether the record has been processed
  390. */
  391. public function log($level, $message, array $context = array())
  392. {
  393. $level = static::toMonologLevel($level);
  394. return $this->addRecord($level, $message, $context);
  395. }
  396. /**
  397. * Adds a log record at the DEBUG level.
  398. *
  399. * This method allows for compatibility with common interfaces.
  400. *
  401. * @param string $message The log message
  402. * @param array $context The log context
  403. * @return Boolean Whether the record has been processed
  404. */
  405. public function debug($message, array $context = array())
  406. {
  407. return $this->addRecord(static::DEBUG, $message, $context);
  408. }
  409. /**
  410. * Adds a log record at the INFO level.
  411. *
  412. * This method allows for compatibility with common interfaces.
  413. *
  414. * @param string $message The log message
  415. * @param array $context The log context
  416. * @return Boolean Whether the record has been processed
  417. */
  418. public function info($message, array $context = array())
  419. {
  420. return $this->addRecord(static::INFO, $message, $context);
  421. }
  422. /**
  423. * Adds a log record at the NOTICE level.
  424. *
  425. * This method allows for compatibility with common interfaces.
  426. *
  427. * @param string $message The log message
  428. * @param array $context The log context
  429. * @return Boolean Whether the record has been processed
  430. */
  431. public function notice($message, array $context = array())
  432. {
  433. return $this->addRecord(static::NOTICE, $message, $context);
  434. }
  435. /**
  436. * Adds a log record at the WARNING level.
  437. *
  438. * This method allows for compatibility with common interfaces.
  439. *
  440. * @param string $message The log message
  441. * @param array $context The log context
  442. * @return Boolean Whether the record has been processed
  443. */
  444. public function warn($message, array $context = array())
  445. {
  446. return $this->addRecord(static::WARNING, $message, $context);
  447. }
  448. /**
  449. * Adds a log record at the WARNING level.
  450. *
  451. * This method allows for compatibility with common interfaces.
  452. *
  453. * @param string $message The log message
  454. * @param array $context The log context
  455. * @return Boolean Whether the record has been processed
  456. */
  457. public function warning($message, array $context = array())
  458. {
  459. return $this->addRecord(static::WARNING, $message, $context);
  460. }
  461. /**
  462. * Adds a log record at the ERROR level.
  463. *
  464. * This method allows for compatibility with common interfaces.
  465. *
  466. * @param string $message The log message
  467. * @param array $context The log context
  468. * @return Boolean Whether the record has been processed
  469. */
  470. public function err($message, array $context = array())
  471. {
  472. return $this->addRecord(static::ERROR, $message, $context);
  473. }
  474. /**
  475. * Adds a log record at the ERROR level.
  476. *
  477. * This method allows for compatibility with common interfaces.
  478. *
  479. * @param string $message The log message
  480. * @param array $context The log context
  481. * @return Boolean Whether the record has been processed
  482. */
  483. public function error($message, array $context = array())
  484. {
  485. return $this->addRecord(static::ERROR, $message, $context);
  486. }
  487. /**
  488. * Adds a log record at the CRITICAL level.
  489. *
  490. * This method allows for compatibility with common interfaces.
  491. *
  492. * @param string $message The log message
  493. * @param array $context The log context
  494. * @return Boolean Whether the record has been processed
  495. */
  496. public function crit($message, array $context = array())
  497. {
  498. return $this->addRecord(static::CRITICAL, $message, $context);
  499. }
  500. /**
  501. * Adds a log record at the CRITICAL level.
  502. *
  503. * This method allows for compatibility with common interfaces.
  504. *
  505. * @param string $message The log message
  506. * @param array $context The log context
  507. * @return Boolean Whether the record has been processed
  508. */
  509. public function critical($message, array $context = array())
  510. {
  511. return $this->addRecord(static::CRITICAL, $message, $context);
  512. }
  513. /**
  514. * Adds a log record at the ALERT level.
  515. *
  516. * This method allows for compatibility with common interfaces.
  517. *
  518. * @param string $message The log message
  519. * @param array $context The log context
  520. * @return Boolean Whether the record has been processed
  521. */
  522. public function alert($message, array $context = array())
  523. {
  524. return $this->addRecord(static::ALERT, $message, $context);
  525. }
  526. /**
  527. * Adds a log record at the EMERGENCY level.
  528. *
  529. * This method allows for compatibility with common interfaces.
  530. *
  531. * @param string $message The log message
  532. * @param array $context The log context
  533. * @return Boolean Whether the record has been processed
  534. */
  535. public function emerg($message, array $context = array())
  536. {
  537. return $this->addRecord(static::EMERGENCY, $message, $context);
  538. }
  539. /**
  540. * Adds a log record at the EMERGENCY level.
  541. *
  542. * This method allows for compatibility with common interfaces.
  543. *
  544. * @param string $message The log message
  545. * @param array $context The log context
  546. * @return Boolean Whether the record has been processed
  547. */
  548. public function emergency($message, array $context = array())
  549. {
  550. return $this->addRecord(static::EMERGENCY, $message, $context);
  551. }
  552. /**
  553. * Set the timezone to be used for the timestamp of log records.
  554. *
  555. * This is stored globally for all Logger instances
  556. *
  557. * @param \DateTimeZone $tz Timezone object
  558. */
  559. public static function setTimezone(\DateTimeZone $tz)
  560. {
  561. self::$timezone = $tz;
  562. }
  563. }