1<?php 2 3namespace MediaWiki\Logger; 4 5use Psr\Log\AbstractLogger; 6use Psr\Log\LoggerInterface; 7 8/** 9 * Wraps another spi to capture all logs generated. This can be 10 * used, for example, to collect all logs generated during a 11 * unit test and report them when the test fails. 12 */ 13class LogCapturingSpi implements Spi { 14 /** @var LoggerInterface[] */ 15 private $singletons; 16 /** @var Spi */ 17 private $inner; 18 /** @var array */ 19 private $logs = []; 20 21 public function __construct( Spi $inner ) { 22 $this->inner = $inner; 23 } 24 25 /** 26 * @return array 27 */ 28 public function getLogs() { 29 return $this->logs; 30 } 31 32 /** 33 * @param string $channel 34 * @return LoggerInterface 35 */ 36 public function getLogger( $channel ) { 37 if ( !isset( $this->singletons[$channel] ) ) { 38 $this->singletons[$channel] = $this->createLogger( $channel ); 39 } 40 return $this->singletons[$channel]; 41 } 42 43 /** 44 * @param array $log 45 */ 46 public function capture( $log ) { 47 $this->logs[] = $log; 48 } 49 50 /** 51 * @param string $channel 52 * @return LoggerInterface 53 */ 54 private function createLogger( $channel ) { 55 $inner = $this->inner->getLogger( $channel ); 56 return new class( $channel, $inner, $this ) extends AbstractLogger { 57 /** @var string */ 58 private $channel; 59 /** @var LoggerInterface */ 60 private $logger; 61 /** @var LogCapturingSpi */ 62 private $parent; 63 64 public function __construct( $channel, LoggerInterface $logger, LogCapturingSpi $parent ) { 65 $this->channel = $channel; 66 $this->logger = $logger; 67 $this->parent = $parent; 68 } 69 70 public function log( $level, $message, array $context = [] ) { 71 $this->parent->capture( [ 72 'channel' => $this->channel, 73 'level' => $level, 74 'message' => $message, 75 'context' => $context 76 ] ); 77 $this->logger->log( $level, $message, $context ); 78 } 79 }; 80 } 81 82 /** 83 * @internal For use by MediaWikiIntegrationTestCase 84 * @return Spi 85 */ 86 public function getInnerSpi(): Spi { 87 return $this->inner; 88 } 89 90 /** 91 * @internal For use by MediaWikiIntegrationTestCase 92 * @param string $channel 93 * @param LoggerInterface|null $logger 94 * @return LoggerInterface|null 95 */ 96 public function setLoggerForTest( $channel, LoggerInterface $logger = null ) { 97 $ret = $this->singletons[$channel] ?? null; 98 $this->singletons[$channel] = $logger; 99 return $ret; 100 } 101} 102