Use interwiki cache directly to resolve transwiki import sources
[lhc/web/wiklou.git] / includes / debug / logger / MonologSpi.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21 namespace MediaWiki\Logger;
22
23 use Monolog\Logger;
24 use ObjectFactory;
25
26 /**
27 * LoggerFactory service provider that creates loggers implemented by
28 * Monolog.
29 *
30 * Configured using an array of configuration data with the keys 'loggers',
31 * 'processors', 'handlers' and 'formatters'.
32 *
33 * The ['loggers']['\@default'] configuration will be used to create loggers
34 * for any channel that isn't explicitly named in the 'loggers' configuration
35 * section.
36 *
37 * Configuration will most typically be provided in the $wgMWLoggerDefaultSpi
38 * global configuration variable used by LoggerFactory to construct its
39 * default SPI provider:
40 * @code
41 * $wgMWLoggerDefaultSpi = array(
42 * 'class' => '\\MediaWiki\\Logger\\MonologSpi',
43 * 'args' => array( array(
44 * 'loggers' => array(
45 * '@default' => array(
46 * 'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ),
47 * 'handlers' => array( 'stream' ),
48 * ),
49 * 'runJobs' => array(
50 * 'processors' => array( 'wiki', 'psr', 'pid' ),
51 * 'handlers' => array( 'stream' ),
52 * )
53 * ),
54 * 'processors' => array(
55 * 'wiki' => array(
56 * 'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
57 * ),
58 * 'psr' => array(
59 * 'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
60 * ),
61 * 'pid' => array(
62 * 'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
63 * ),
64 * 'uid' => array(
65 * 'class' => '\\Monolog\\Processor\\UidProcessor',
66 * ),
67 * 'web' => array(
68 * 'class' => '\\Monolog\\Processor\\WebProcessor',
69 * ),
70 * ),
71 * 'handlers' => array(
72 * 'stream' => array(
73 * 'class' => '\\Monolog\\Handler\\StreamHandler',
74 * 'args' => array( 'path/to/your.log' ),
75 * 'formatter' => 'line',
76 * ),
77 * 'redis' => array(
78 * 'class' => '\\Monolog\\Handler\\RedisHandler',
79 * 'args' => array( function() {
80 * $redis = new Redis();
81 * $redis->connect( '127.0.0.1', 6379 );
82 * return $redis;
83 * },
84 * 'logstash'
85 * ),
86 * 'formatter' => 'logstash',
87 * ),
88 * 'udp2log' => array(
89 * 'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
90 * 'args' => array(
91 * 'udp://127.0.0.1:8420/mediawiki
92 * ),
93 * 'formatter' => 'line',
94 * ),
95 * ),
96 * 'formatters' => array(
97 * 'line' => array(
98 * 'class' => '\\Monolog\\Formatter\\LineFormatter',
99 * ),
100 * 'logstash' => array(
101 * 'class' => '\\Monolog\\Formatter\\LogstashFormatter',
102 * 'args' => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ),
103 * ),
104 * ),
105 * ) ),
106 * );
107 * @endcode
108 *
109 * @see https://github.com/Seldaek/monolog
110 * @since 1.25
111 * @author Bryan Davis <bd808@wikimedia.org>
112 * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
113 */
114 class MonologSpi implements Spi {
115
116 /**
117 * @var array $singletons
118 */
119 protected $singletons;
120
121 /**
122 * Configuration for creating new loggers.
123 * @var array $config
124 */
125 protected $config;
126
127
128 /**
129 * @param array $config Configuration data.
130 */
131 public function __construct( array $config ) {
132 $this->config = array();
133 $this->mergeConfig( $config );
134 }
135
136
137 /**
138 * Merge additional configuration data into the configuration.
139 *
140 * @since 1.26
141 * @param array $config Configuration data.
142 */
143 public function mergeConfig( array $config ) {
144 foreach ( $config as $key => $value ) {
145 if ( isset( $this->config[$key] ) ) {
146 $this->config[$key] = array_merge( $this->config[$key], $value );
147 } else {
148 $this->config[$key] = $value;
149 }
150 }
151 $this->reset();
152 }
153
154
155 /**
156 * Reset internal caches.
157 *
158 * This is public for use in unit tests. Under normal operation there should
159 * be no need to flush the caches.
160 */
161 public function reset() {
162 $this->singletons = array(
163 'loggers' => array(),
164 'handlers' => array(),
165 'formatters' => array(),
166 'processors' => array(),
167 );
168 }
169
170
171 /**
172 * Get a logger instance.
173 *
174 * Creates and caches a logger instance based on configuration found in the
175 * $wgMWLoggerMonologSpiConfig global. Subsequent request for the same channel
176 * name will return the cached instance.
177 *
178 * @param string $channel Logging channel
179 * @return \\Psr\\Log\\LoggerInterface Logger instance
180 */
181 public function getLogger( $channel ) {
182 if ( !isset( $this->singletons['loggers'][$channel] ) ) {
183 // Fallback to using the '@default' configuration if an explict
184 // configuration for the requested channel isn't found.
185 $spec = isset( $this->config['loggers'][$channel] ) ?
186 $this->config['loggers'][$channel] :
187 $this->config['loggers']['@default'];
188
189 $monolog = $this->createLogger( $channel, $spec );
190 $this->singletons['loggers'][$channel] = $monolog;
191 }
192
193 return $this->singletons['loggers'][$channel];
194 }
195
196
197 /**
198 * Create a logger.
199 * @param string $channel Logger channel
200 * @param array $spec Configuration
201 * @return \\Monolog\\Logger
202 */
203 protected function createLogger( $channel, $spec ) {
204 $obj = new Logger( $channel );
205
206 if ( isset( $spec['processors'] ) ) {
207 foreach ( $spec['processors'] as $processor ) {
208 $obj->pushProcessor( $this->getProcessor( $processor ) );
209 }
210 }
211
212 if ( isset( $spec['handlers'] ) ) {
213 foreach ( $spec['handlers'] as $handler ) {
214 $obj->pushHandler( $this->getHandler( $handler ) );
215 }
216 }
217 return $obj;
218 }
219
220
221 /**
222 * Create or return cached processor.
223 * @param string $name Processor name
224 * @return callable
225 */
226 public function getProcessor( $name ) {
227 if ( !isset( $this->singletons['processors'][$name] ) ) {
228 $spec = $this->config['processors'][$name];
229 $processor = ObjectFactory::getObjectFromSpec( $spec );
230 $this->singletons['processors'][$name] = $processor;
231 }
232 return $this->singletons['processors'][$name];
233 }
234
235
236 /**
237 * Create or return cached handler.
238 * @param string $name Processor name
239 * @return \\Monolog\\Handler\\HandlerInterface
240 */
241 public function getHandler( $name ) {
242 if ( !isset( $this->singletons['handlers'][$name] ) ) {
243 $spec = $this->config['handlers'][$name];
244 $handler = ObjectFactory::getObjectFromSpec( $spec );
245 if ( isset( $spec['formatter'] ) ) {
246 $handler->setFormatter(
247 $this->getFormatter( $spec['formatter'] )
248 );
249 }
250 $this->singletons['handlers'][$name] = $handler;
251 }
252 return $this->singletons['handlers'][$name];
253 }
254
255
256 /**
257 * Create or return cached formatter.
258 * @param string $name Formatter name
259 * @return \\Monolog\\Formatter\\FormatterInterface
260 */
261 public function getFormatter( $name ) {
262 if ( !isset( $this->singletons['formatters'][$name] ) ) {
263 $spec = $this->config['formatters'][$name];
264 $formatter = ObjectFactory::getObjectFromSpec( $spec );
265 $this->singletons['formatters'][$name] = $formatter;
266 }
267 return $this->singletons['formatters'][$name];
268 }
269 }