2
0

LoggerTest.php 28 KB

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