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 // phpcs:ignore MediaWiki.Usage.NestedFunctions.NestedFunction 65 public function __construct( $channel, LoggerInterface $logger, LogCapturingSpi $parent ) { 66 $this->channel = $channel; 67 $this->logger = $logger; 68 $this->parent = $parent; 69 } 70 71 // phpcs:ignore MediaWiki.Usage.NestedFunctions.NestedFunction 72 public function log( $level, $message, array $context = [] ) { 73 $this->parent->capture( [ 74 'channel' => $this->channel, 75 'level' => $level, 76 'message' => $message, 77 'context' => $context 78 ] ); 79 $this->logger->log( $level, $message, $context ); 80 } 81 }; 82 } 83 84 /** 85 * @internal For use by MediaWikiIntegrationTestCase 86 * @return Spi 87 */ 88 public function getInnerSpi() : Spi { 89 return $this->inner; 90 } 91 92 /** 93 * @internal For use by MediaWikiIntegrationTestCase 94 * @param string $channel 95 * @param LoggerInterface|null $logger 96 * @return LoggerInterface|null 97 */ 98 public function setLoggerForTest( $channel, LoggerInterface $logger = null ) { 99 $ret = $this->singletons[$channel] ?? null; 100 $this->singletons[$channel] = $logger; 101 return $ret; 102 } 103} 104