LoggerTest.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. <?php declare(strict_types=1);
  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\Processor\WebProcessor;
  13. use Monolog\Handler\TestHandler;
  14. class LoggerTest extends \PHPUnit\Framework\TestCase
  15. {
  16. /**
  17. * @covers Monolog\Logger::getName
  18. */
  19. public function testGetName()
  20. {
  21. $logger = new Logger('foo');
  22. $this->assertEquals('foo', $logger->getName());
  23. }
  24. /**
  25. * @covers Monolog\Logger::getLevelName
  26. */
  27. public function testGetLevelName()
  28. {
  29. $this->assertEquals('ERROR', Logger::getLevelName(Logger::ERROR));
  30. }
  31. /**
  32. * @covers Monolog\Logger::withName
  33. */
  34. public function testWithName()
  35. {
  36. $first = new Logger('first', [$handler = new TestHandler()]);
  37. $second = $first->withName('second');
  38. $this->assertSame('first', $first->getName());
  39. $this->assertSame('second', $second->getName());
  40. $this->assertSame($handler, $second->popHandler());
  41. }
  42. /**
  43. * @covers Monolog\Logger::toMonologLevel
  44. */
  45. public function testConvertPSR3ToMonologLevel()
  46. {
  47. $this->assertEquals(Logger::toMonologLevel('debug'), 100);
  48. $this->assertEquals(Logger::toMonologLevel('info'), 200);
  49. $this->assertEquals(Logger::toMonologLevel('notice'), 250);
  50. $this->assertEquals(Logger::toMonologLevel('warning'), 300);
  51. $this->assertEquals(Logger::toMonologLevel('error'), 400);
  52. $this->assertEquals(Logger::toMonologLevel('critical'), 500);
  53. $this->assertEquals(Logger::toMonologLevel('alert'), 550);
  54. $this->assertEquals(Logger::toMonologLevel('emergency'), 600);
  55. }
  56. /**
  57. * @covers Monolog\Logger::addRecord
  58. * @covers Monolog\Logger::log
  59. */
  60. public function testConvertRFC5424ToMonologLevelInAddRecordAndLog()
  61. {
  62. $logger = new Logger('test');
  63. $handler = new TestHandler;
  64. $logger->pushHandler($handler);
  65. foreach ([
  66. 7 => 100,
  67. 6 => 200,
  68. 5 => 250,
  69. 4 => 300,
  70. 3 => 400,
  71. 2 => 500,
  72. 1 => 550,
  73. 0 => 600,
  74. ] as $rfc5424Level => $monologLevel) {
  75. $handler->reset();
  76. $logger->addRecord($rfc5424Level, 'test');
  77. $logger->log($rfc5424Level, 'test');
  78. $records = $handler->getRecords();
  79. self::assertCount(2, $records);
  80. self::assertSame($monologLevel, $records[0]['level']);
  81. self::assertSame($monologLevel, $records[1]['level']);
  82. }
  83. }
  84. /**
  85. * @covers Monolog\Logger::getLevelName
  86. */
  87. public function testGetLevelNameThrows()
  88. {
  89. $this->expectException(\InvalidArgumentException::class);
  90. Logger::getLevelName(5);
  91. }
  92. /**
  93. * @covers Monolog\Logger::__construct
  94. */
  95. public function testChannel()
  96. {
  97. $logger = new Logger('foo');
  98. $handler = new TestHandler;
  99. $logger->pushHandler($handler);
  100. $logger->warning('test');
  101. list($record) = $handler->getRecords();
  102. $this->assertEquals('foo', $record['channel']);
  103. }
  104. /**
  105. * @covers Monolog\Logger::addRecord
  106. */
  107. public function testLogPreventsCircularLogging()
  108. {
  109. $logger = new Logger(__METHOD__);
  110. $loggingHandler = new LoggingHandler($logger);
  111. $testHandler = new TestHandler();
  112. $logger->pushHandler($loggingHandler);
  113. $logger->pushHandler($testHandler);
  114. $logger->addRecord(Logger::ALERT, 'test');
  115. $records = $testHandler->getRecords();
  116. $this->assertCount(3, $records);
  117. $this->assertSame('ALERT', $records[0]['level_name']);
  118. $this->assertSame('DEBUG', $records[1]['level_name']);
  119. $this->assertSame('WARNING', $records[2]['level_name']);
  120. }
  121. /**
  122. * @covers Monolog\Logger::addRecord
  123. */
  124. public function testLog()
  125. {
  126. $logger = new Logger(__METHOD__);
  127. $handler = $this->prophesize('Monolog\Handler\NullHandler');
  128. $handler->handle(\Prophecy\Argument::any())->shouldBeCalled();
  129. $handler->isHandling(['level' => 300])->willReturn(true);
  130. $logger->pushHandler($handler->reveal());
  131. $this->assertTrue($logger->addRecord(Logger::WARNING, 'test'));
  132. }
  133. /**
  134. * @covers Monolog\Logger::addRecord
  135. */
  136. public function testLogNotHandled()
  137. {
  138. $logger = new Logger(__METHOD__);
  139. $handler = $this->prophesize('Monolog\Handler\NullHandler');
  140. $handler->handle()->shouldNotBeCalled();
  141. $handler->isHandling(['level' => 300])->willReturn(false);
  142. $logger->pushHandler($handler->reveal());
  143. $this->assertFalse($logger->addRecord(Logger::WARNING, 'test'));
  144. }
  145. public function testHandlersInCtor()
  146. {
  147. $handler1 = new TestHandler;
  148. $handler2 = new TestHandler;
  149. $logger = new Logger(__METHOD__, [$handler1, $handler2]);
  150. $this->assertEquals($handler1, $logger->popHandler());
  151. $this->assertEquals($handler2, $logger->popHandler());
  152. }
  153. public function testProcessorsInCtor()
  154. {
  155. $processor1 = new WebProcessor;
  156. $processor2 = new WebProcessor;
  157. $logger = new Logger(__METHOD__, [], [$processor1, $processor2]);
  158. $this->assertEquals($processor1, $logger->popProcessor());
  159. $this->assertEquals($processor2, $logger->popProcessor());
  160. }
  161. /**
  162. * @covers Monolog\Logger::pushHandler
  163. * @covers Monolog\Logger::popHandler
  164. */
  165. public function testPushPopHandler()
  166. {
  167. $logger = new Logger(__METHOD__);
  168. $handler1 = new TestHandler;
  169. $handler2 = new TestHandler;
  170. $logger->pushHandler($handler1);
  171. $logger->pushHandler($handler2);
  172. $this->assertEquals($handler2, $logger->popHandler());
  173. $this->assertEquals($handler1, $logger->popHandler());
  174. $this->expectException(\LogicException::class);
  175. $logger->popHandler();
  176. }
  177. /**
  178. * @covers Monolog\Logger::setHandlers
  179. */
  180. public function testSetHandlers()
  181. {
  182. $logger = new Logger(__METHOD__);
  183. $handler1 = new TestHandler;
  184. $handler2 = new TestHandler;
  185. $logger->pushHandler($handler1);
  186. $logger->setHandlers([$handler2]);
  187. // handler1 has been removed
  188. $this->assertEquals([$handler2], $logger->getHandlers());
  189. $logger->setHandlers([
  190. "AMapKey" => $handler1,
  191. "Woop" => $handler2,
  192. ]);
  193. // Keys have been scrubbed
  194. $this->assertEquals([$handler1, $handler2], $logger->getHandlers());
  195. }
  196. /**
  197. * @covers Monolog\Logger::pushProcessor
  198. * @covers Monolog\Logger::popProcessor
  199. */
  200. public function testPushPopProcessor()
  201. {
  202. $logger = new Logger(__METHOD__);
  203. $processor1 = new WebProcessor;
  204. $processor2 = new WebProcessor;
  205. $logger->pushProcessor($processor1);
  206. $logger->pushProcessor($processor2);
  207. $this->assertEquals($processor2, $logger->popProcessor());
  208. $this->assertEquals($processor1, $logger->popProcessor());
  209. $this->expectException(\LogicException::class);
  210. $logger->popProcessor();
  211. }
  212. /**
  213. * @covers Monolog\Logger::addRecord
  214. */
  215. public function testProcessorsAreExecuted()
  216. {
  217. $logger = new Logger(__METHOD__);
  218. $handler = new TestHandler;
  219. $logger->pushHandler($handler);
  220. $logger->pushProcessor(function ($record) {
  221. $record['extra']['win'] = true;
  222. return $record;
  223. });
  224. $logger->error('test');
  225. list($record) = $handler->getRecords();
  226. $this->assertTrue($record['extra']['win']);
  227. }
  228. /**
  229. * @covers Monolog\Logger::addRecord
  230. */
  231. public function testProcessorsAreCalledOnlyOnce()
  232. {
  233. $logger = new Logger(__METHOD__);
  234. $handler = $this->createMock('Monolog\Handler\HandlerInterface');
  235. $handler->expects($this->any())
  236. ->method('isHandling')
  237. ->will($this->returnValue(true))
  238. ;
  239. $handler->expects($this->any())
  240. ->method('handle')
  241. ->will($this->returnValue(true))
  242. ;
  243. $logger->pushHandler($handler);
  244. $processor = $this->getMockBuilder('Monolog\Processor\WebProcessor')
  245. ->disableOriginalConstructor()
  246. ->onlyMethods(['__invoke'])
  247. ->getMock()
  248. ;
  249. $processor->expects($this->once())
  250. ->method('__invoke')
  251. ->will($this->returnArgument(0))
  252. ;
  253. $logger->pushProcessor($processor);
  254. $logger->error('test');
  255. }
  256. /**
  257. * @covers Monolog\Logger::addRecord
  258. */
  259. public function testProcessorsNotCalledWhenNotHandled()
  260. {
  261. $logger = new Logger(__METHOD__);
  262. $handler = $this->createMock('Monolog\Handler\HandlerInterface');
  263. $handler->expects($this->once())
  264. ->method('isHandling')
  265. ->will($this->returnValue(false))
  266. ;
  267. $logger->pushHandler($handler);
  268. $that = $this;
  269. $logger->pushProcessor(function ($record) use ($that) {
  270. $that->fail('The processor should not be called');
  271. });
  272. $logger->alert('test');
  273. }
  274. /**
  275. * @covers Monolog\Logger::addRecord
  276. */
  277. public function testHandlersNotCalledBeforeFirstHandling()
  278. {
  279. $logger = new Logger(__METHOD__);
  280. $handler1 = $this->createMock('Monolog\Handler\HandlerInterface');
  281. $handler1->expects($this->never())
  282. ->method('isHandling')
  283. ->will($this->returnValue(false))
  284. ;
  285. $handler1->expects($this->once())
  286. ->method('handle')
  287. ->will($this->returnValue(false))
  288. ;
  289. $logger->pushHandler($handler1);
  290. $handler2 = $this->createMock('Monolog\Handler\HandlerInterface');
  291. $handler2->expects($this->once())
  292. ->method('isHandling')
  293. ->will($this->returnValue(true))
  294. ;
  295. $handler2->expects($this->once())
  296. ->method('handle')
  297. ->will($this->returnValue(false))
  298. ;
  299. $logger->pushHandler($handler2);
  300. $handler3 = $this->createMock('Monolog\Handler\HandlerInterface');
  301. $handler3->expects($this->once())
  302. ->method('isHandling')
  303. ->will($this->returnValue(false))
  304. ;
  305. $handler3->expects($this->never())
  306. ->method('handle')
  307. ;
  308. $logger->pushHandler($handler3);
  309. $logger->debug('test');
  310. }
  311. /**
  312. * @covers Monolog\Logger::addRecord
  313. */
  314. public function testHandlersNotCalledBeforeFirstHandlingWithAssocArray()
  315. {
  316. $handler1 = $this->createMock('Monolog\Handler\HandlerInterface');
  317. $handler1->expects($this->never())
  318. ->method('isHandling')
  319. ->will($this->returnValue(false))
  320. ;
  321. $handler1->expects($this->once())
  322. ->method('handle')
  323. ->will($this->returnValue(false))
  324. ;
  325. $handler2 = $this->createMock('Monolog\Handler\HandlerInterface');
  326. $handler2->expects($this->once())
  327. ->method('isHandling')
  328. ->will($this->returnValue(true))
  329. ;
  330. $handler2->expects($this->once())
  331. ->method('handle')
  332. ->will($this->returnValue(false))
  333. ;
  334. $handler3 = $this->createMock('Monolog\Handler\HandlerInterface');
  335. $handler3->expects($this->once())
  336. ->method('isHandling')
  337. ->will($this->returnValue(false))
  338. ;
  339. $handler3->expects($this->never())
  340. ->method('handle')
  341. ;
  342. $logger = new Logger(__METHOD__, ['last' => $handler3, 'second' => $handler2, 'first' => $handler1]);
  343. $logger->debug('test');
  344. }
  345. /**
  346. * @covers Monolog\Logger::addRecord
  347. */
  348. public function testBubblingWhenTheHandlerReturnsFalse()
  349. {
  350. $logger = new Logger(__METHOD__);
  351. $handler1 = $this->createMock('Monolog\Handler\HandlerInterface');
  352. $handler1->expects($this->any())
  353. ->method('isHandling')
  354. ->will($this->returnValue(true))
  355. ;
  356. $handler1->expects($this->once())
  357. ->method('handle')
  358. ->will($this->returnValue(false))
  359. ;
  360. $logger->pushHandler($handler1);
  361. $handler2 = $this->createMock('Monolog\Handler\HandlerInterface');
  362. $handler2->expects($this->any())
  363. ->method('isHandling')
  364. ->will($this->returnValue(true))
  365. ;
  366. $handler2->expects($this->once())
  367. ->method('handle')
  368. ->will($this->returnValue(false))
  369. ;
  370. $logger->pushHandler($handler2);
  371. $logger->debug('test');
  372. }
  373. /**
  374. * @covers Monolog\Logger::addRecord
  375. */
  376. public function testNotBubblingWhenTheHandlerReturnsTrue()
  377. {
  378. $logger = new Logger(__METHOD__);
  379. $handler1 = $this->createMock('Monolog\Handler\HandlerInterface');
  380. $handler1->expects($this->any())
  381. ->method('isHandling')
  382. ->will($this->returnValue(true))
  383. ;
  384. $handler1->expects($this->never())
  385. ->method('handle')
  386. ;
  387. $logger->pushHandler($handler1);
  388. $handler2 = $this->createMock('Monolog\Handler\HandlerInterface');
  389. $handler2->expects($this->any())
  390. ->method('isHandling')
  391. ->will($this->returnValue(true))
  392. ;
  393. $handler2->expects($this->once())
  394. ->method('handle')
  395. ->will($this->returnValue(true))
  396. ;
  397. $logger->pushHandler($handler2);
  398. $logger->debug('test');
  399. }
  400. /**
  401. * @covers Monolog\Logger::isHandling
  402. */
  403. public function testIsHandling()
  404. {
  405. $logger = new Logger(__METHOD__);
  406. $handler1 = $this->createMock('Monolog\Handler\HandlerInterface');
  407. $handler1->expects($this->any())
  408. ->method('isHandling')
  409. ->will($this->returnValue(false))
  410. ;
  411. $logger->pushHandler($handler1);
  412. $this->assertFalse($logger->isHandling(Logger::DEBUG));
  413. $handler2 = $this->createMock('Monolog\Handler\HandlerInterface');
  414. $handler2->expects($this->any())
  415. ->method('isHandling')
  416. ->will($this->returnValue(true))
  417. ;
  418. $logger->pushHandler($handler2);
  419. $this->assertTrue($logger->isHandling(Logger::DEBUG));
  420. }
  421. /**
  422. * @dataProvider logMethodProvider
  423. * @covers Monolog\Logger::debug
  424. * @covers Monolog\Logger::info
  425. * @covers Monolog\Logger::notice
  426. * @covers Monolog\Logger::warning
  427. * @covers Monolog\Logger::error
  428. * @covers Monolog\Logger::critical
  429. * @covers Monolog\Logger::alert
  430. * @covers Monolog\Logger::emergency
  431. */
  432. public function testLogMethods($method, $expectedLevel)
  433. {
  434. $logger = new Logger('foo');
  435. $handler = new TestHandler;
  436. $logger->pushHandler($handler);
  437. $logger->{$method}('test');
  438. list($record) = $handler->getRecords();
  439. $this->assertEquals($expectedLevel, $record['level']);
  440. }
  441. public function logMethodProvider()
  442. {
  443. return [
  444. // PSR-3 methods
  445. ['debug', Logger::DEBUG],
  446. ['info', Logger::INFO],
  447. ['notice', Logger::NOTICE],
  448. ['warning', Logger::WARNING],
  449. ['error', Logger::ERROR],
  450. ['critical', Logger::CRITICAL],
  451. ['alert', Logger::ALERT],
  452. ['emergency', Logger::EMERGENCY],
  453. ];
  454. }
  455. /**
  456. * @dataProvider setTimezoneProvider
  457. * @covers Monolog\Logger::setTimezone
  458. */
  459. public function testSetTimezone($tz)
  460. {
  461. $logger = new Logger('foo');
  462. $logger->setTimezone($tz);
  463. $handler = new TestHandler;
  464. $logger->pushHandler($handler);
  465. $logger->info('test');
  466. list($record) = $handler->getRecords();
  467. $this->assertEquals($tz, $record['datetime']->getTimezone());
  468. }
  469. public function setTimezoneProvider()
  470. {
  471. return array_map(
  472. function ($tz) {
  473. return [new \DateTimeZone($tz)];
  474. },
  475. \DateTimeZone::listIdentifiers()
  476. );
  477. }
  478. /**
  479. * @covers Monolog\Logger::setTimezone
  480. * @covers Monolog\DateTimeImmutable::__construct
  481. */
  482. public function testTimezoneIsRespectedInUTC()
  483. {
  484. foreach ([true, false] as $microseconds) {
  485. $logger = new Logger('foo');
  486. $logger->useMicrosecondTimestamps($microseconds);
  487. $tz = new \DateTimeZone('America/New_York');
  488. $logger->setTimezone($tz);
  489. $handler = new TestHandler;
  490. $logger->pushHandler($handler);
  491. $dt = new \DateTime('now', $tz);
  492. $logger->info('test');
  493. list($record) = $handler->getRecords();
  494. $this->assertEquals($tz, $record['datetime']->getTimezone());
  495. $this->assertEquals($dt->format('Y/m/d H:i'), $record['datetime']->format('Y/m/d H:i'), 'Time should match timezone with microseconds set to: '.var_export($microseconds, true));
  496. }
  497. }
  498. /**
  499. * @covers Monolog\Logger::setTimezone
  500. * @covers Monolog\DateTimeImmutable::__construct
  501. */
  502. public function testTimezoneIsRespectedInOtherTimezone()
  503. {
  504. date_default_timezone_set('CET');
  505. foreach ([true, false] as $microseconds) {
  506. $logger = new Logger('foo');
  507. $logger->useMicrosecondTimestamps($microseconds);
  508. $tz = new \DateTimeZone('America/New_York');
  509. $logger->setTimezone($tz);
  510. $handler = new TestHandler;
  511. $logger->pushHandler($handler);
  512. $dt = new \DateTime('now', $tz);
  513. $logger->info('test');
  514. list($record) = $handler->getRecords();
  515. $this->assertEquals($tz, $record['datetime']->getTimezone());
  516. $this->assertEquals($dt->format('Y/m/d H:i'), $record['datetime']->format('Y/m/d H:i'), 'Time should match timezone with microseconds set to: '.var_export($microseconds, true));
  517. }
  518. }
  519. public function tearDown(): void
  520. {
  521. date_default_timezone_set('UTC');
  522. }
  523. /**
  524. * @dataProvider useMicrosecondTimestampsProvider
  525. * @covers Monolog\Logger::useMicrosecondTimestamps
  526. * @covers Monolog\Logger::addRecord
  527. */
  528. public function testUseMicrosecondTimestamps($micro, $assert, $assertFormat)
  529. {
  530. if (PHP_VERSION_ID === 70103) {
  531. $this->markTestSkipped();
  532. }
  533. $logger = new Logger('foo');
  534. $logger->useMicrosecondTimestamps($micro);
  535. $handler = new TestHandler;
  536. $logger->pushHandler($handler);
  537. $logger->info('test');
  538. list($record) = $handler->getRecords();
  539. $this->{$assert}('000000', $record['datetime']->format('u'));
  540. $this->assertSame($record['datetime']->format($assertFormat), (string) $record['datetime']);
  541. }
  542. public function useMicrosecondTimestampsProvider()
  543. {
  544. return [
  545. // this has a very small chance of a false negative (1/10^6)
  546. 'with microseconds' => [true, 'assertNotSame', 'Y-m-d\TH:i:s.uP'],
  547. // php 7.1 always includes microseconds, so we keep them in, but we format the datetime without
  548. 'without microseconds' => [false, 'assertNotSame', 'Y-m-d\TH:i:sP'],
  549. ];
  550. }
  551. /**
  552. * @covers Monolog\Logger::setExceptionHandler
  553. */
  554. public function testSetExceptionHandler()
  555. {
  556. $logger = new Logger(__METHOD__);
  557. $this->assertNull($logger->getExceptionHandler());
  558. $callback = function ($ex) {
  559. };
  560. $logger->setExceptionHandler($callback);
  561. $this->assertEquals($callback, $logger->getExceptionHandler());
  562. }
  563. /**
  564. * @covers Monolog\Logger::handleException
  565. */
  566. public function testDefaultHandleException()
  567. {
  568. $logger = new Logger(__METHOD__);
  569. $handler = $this->getMockBuilder('Monolog\Handler\HandlerInterface')->getMock();
  570. $handler->expects($this->any())
  571. ->method('isHandling')
  572. ->will($this->returnValue(true))
  573. ;
  574. $handler->expects($this->any())
  575. ->method('handle')
  576. ->will($this->throwException(new \Exception('Some handler exception')))
  577. ;
  578. $this->expectException(\Exception::class);
  579. $logger->pushHandler($handler);
  580. $logger->info('test');
  581. }
  582. /**
  583. * @covers Monolog\Logger::handleException
  584. * @covers Monolog\Logger::addRecord
  585. */
  586. public function testCustomHandleException()
  587. {
  588. $logger = new Logger(__METHOD__);
  589. $that = $this;
  590. $logger->setExceptionHandler(function ($e, $record) use ($that) {
  591. $that->assertEquals($e->getMessage(), 'Some handler exception');
  592. $that->assertTrue(is_array($record));
  593. $that->assertEquals($record['message'], 'test');
  594. });
  595. $handler = $this->getMockBuilder('Monolog\Handler\HandlerInterface')->getMock();
  596. $handler->expects($this->any())
  597. ->method('isHandling')
  598. ->will($this->returnValue(true))
  599. ;
  600. $handler->expects($this->any())
  601. ->method('handle')
  602. ->will($this->throwException(new \Exception('Some handler exception')))
  603. ;
  604. $logger->pushHandler($handler);
  605. $logger->info('test');
  606. }
  607. public function testSerializable()
  608. {
  609. $logger = new Logger(__METHOD__);
  610. self::assertInstanceOf(Logger::class, unserialize(serialize($logger)));
  611. }
  612. public function testReset()
  613. {
  614. $logger = new Logger('app');
  615. $testHandler = new Handler\TestHandler();
  616. $testHandler->setSkipReset(true);
  617. $bufferHandler = new Handler\BufferHandler($testHandler);
  618. $groupHandler = new Handler\GroupHandler(array($bufferHandler));
  619. $fingersCrossedHandler = new Handler\FingersCrossedHandler($groupHandler);
  620. $logger->pushHandler($fingersCrossedHandler);
  621. $processorUid1 = new Processor\UidProcessor(10);
  622. $uid1 = $processorUid1->getUid();
  623. $groupHandler->pushProcessor($processorUid1);
  624. $processorUid2 = new Processor\UidProcessor(5);
  625. $uid2 = $processorUid2->getUid();
  626. $logger->pushProcessor($processorUid2);
  627. $getProperty = function ($object, $property) {
  628. $reflectionProperty = new \ReflectionProperty(get_class($object), $property);
  629. $reflectionProperty->setAccessible(true);
  630. return $reflectionProperty->getValue($object);
  631. };
  632. $that = $this;
  633. $assertBufferOfBufferHandlerEmpty = function () use ($getProperty, $bufferHandler, $that) {
  634. $that->assertEmpty($getProperty($bufferHandler, 'buffer'));
  635. };
  636. $assertBuffersEmpty = function () use ($assertBufferOfBufferHandlerEmpty, $getProperty, $fingersCrossedHandler, $that) {
  637. $assertBufferOfBufferHandlerEmpty();
  638. $that->assertEmpty($getProperty($fingersCrossedHandler, 'buffer'));
  639. };
  640. $logger->debug('debug1');
  641. $logger->reset();
  642. $assertBuffersEmpty();
  643. $this->assertFalse($testHandler->hasDebugRecords());
  644. $this->assertFalse($testHandler->hasErrorRecords());
  645. $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
  646. $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
  647. $logger->debug('debug2');
  648. $logger->error('error2');
  649. $logger->reset();
  650. $assertBuffersEmpty();
  651. $this->assertTrue($testHandler->hasRecordThatContains('debug2', Logger::DEBUG));
  652. $this->assertTrue($testHandler->hasRecordThatContains('error2', Logger::ERROR));
  653. $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
  654. $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
  655. $logger->info('info3');
  656. $this->assertNotEmpty($getProperty($fingersCrossedHandler, 'buffer'));
  657. $assertBufferOfBufferHandlerEmpty();
  658. $this->assertFalse($testHandler->hasInfoRecords());
  659. $logger->reset();
  660. $assertBuffersEmpty();
  661. $this->assertFalse($testHandler->hasInfoRecords());
  662. $this->assertNotSame($uid1, $uid1 = $processorUid1->getUid());
  663. $this->assertNotSame($uid2, $uid2 = $processorUid2->getUid());
  664. $logger->notice('notice4');
  665. $logger->emergency('emergency4');
  666. $logger->reset();
  667. $assertBuffersEmpty();
  668. $this->assertFalse($testHandler->hasInfoRecords());
  669. $this->assertTrue($testHandler->hasRecordThatContains('notice4', Logger::NOTICE));
  670. $this->assertTrue($testHandler->hasRecordThatContains('emergency4', Logger::EMERGENCY));
  671. $this->assertNotSame($uid1, $processorUid1->getUid());
  672. $this->assertNotSame($uid2, $processorUid2->getUid());
  673. }
  674. /**
  675. * @covers Logger::addRecord
  676. */
  677. public function testLogWithDateTime()
  678. {
  679. foreach ([true, false] as $microseconds) {
  680. $logger = new Logger(__METHOD__);
  681. $loggingHandler = new LoggingHandler($logger);
  682. $testHandler = new TestHandler();
  683. $logger->pushHandler($loggingHandler);
  684. $logger->pushHandler($testHandler);
  685. $datetime = (new DateTimeImmutable($microseconds))->modify('2022-03-04 05:06:07');
  686. $logger->addRecord(Logger::DEBUG, 'test', [], $datetime);
  687. list($record) = $testHandler->getRecords();
  688. $this->assertEquals($datetime->format('Y-m-d H:i:s'), $record['datetime']->format('Y-m-d H:i:s'));
  689. }
  690. }
  691. /**
  692. * @requires PHP 8.1
  693. */
  694. public function testLogCycleDetectionWithFibersWithoutCycle()
  695. {
  696. $logger = new Logger(__METHOD__);
  697. $fiberSuspendHandler = new FiberSuspendHandler();
  698. $testHandler = new TestHandler();
  699. $logger->pushHandler($fiberSuspendHandler);
  700. $logger->pushHandler($testHandler);
  701. $fibers = [];
  702. for ($i = 0; $i < 10; $i++) {
  703. $fiber = new \Fiber(static function () use ($logger) {
  704. $logger->info('test');
  705. });
  706. $fiber->start();
  707. // We need to keep a reference here, because otherwise the fiber gets automatically cleaned up
  708. $fibers[] = $fiber;
  709. }
  710. self::assertCount(10, $testHandler->getRecords());
  711. }
  712. /**
  713. * @requires PHP 8.1
  714. */
  715. public function testLogCycleDetectionWithFibersWithCycle()
  716. {
  717. $logger = new Logger(__METHOD__);
  718. $fiberSuspendHandler = new FiberSuspendHandler();
  719. $loggingHandler = new LoggingHandler($logger);
  720. $testHandler = new TestHandler();
  721. $logger->pushHandler($fiberSuspendHandler);
  722. $logger->pushHandler($loggingHandler);
  723. $logger->pushHandler($testHandler);
  724. $fiber = new \Fiber(static function () use ($logger) {
  725. $logger->info('test');
  726. });
  727. $fiber->start();
  728. self::assertCount(3, $testHandler->getRecords());
  729. }
  730. }
  731. class LoggingHandler implements HandlerInterface
  732. {
  733. /**
  734. * @var Logger
  735. */
  736. private $logger;
  737. public function __construct(Logger $logger)
  738. {
  739. $this->logger = $logger;
  740. }
  741. public function isHandling(array $record): bool
  742. {
  743. return true;
  744. }
  745. public function handle(array $record): bool
  746. {
  747. $this->logger->debug('Log triggered while logging');
  748. return false;
  749. }
  750. public function handleBatch(array $records): void
  751. {
  752. }
  753. public function close(): void
  754. {
  755. }
  756. }
  757. class FiberSuspendHandler implements HandlerInterface
  758. {
  759. public function isHandling(array $record): bool
  760. {
  761. return true;
  762. }
  763. public function handle(array $record): bool
  764. {
  765. \Fiber::suspend();
  766. return true;
  767. }
  768. public function handleBatch(array $records): void
  769. {
  770. }
  771. public function close(): void
  772. {
  773. }
  774. }