Merge "Make query pages not accept offset > 10000 in miser mode"
[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 MediaWiki\Logger\Monolog\BufferHandler;
24 use Monolog\Logger;
25 use ObjectFactory;
26
27 /**
28 * LoggerFactory service provider that creates loggers implemented by
29 * Monolog.
30 *
31 * Configured using an array of configuration data with the keys 'loggers',
32 * 'processors', 'handlers' and 'formatters'.
33 *
34 * The ['loggers']['\@default'] configuration will be used to create loggers
35 * for any channel that isn't explicitly named in the 'loggers' configuration
36 * section.
37 *
38 * Configuration will most typically be provided in the $wgMWLoggerDefaultSpi
39 * global configuration variable used by LoggerFactory to construct its
40 * default SPI provider:
41 * @code
42 * $wgMWLoggerDefaultSpi = array(
43 * 'class' => '\\MediaWiki\\Logger\\MonologSpi',
44 * 'args' => array( array(
45 * 'loggers' => array(
46 * '@default' => array(
47 * 'processors' => array( 'wiki', 'psr', 'pid', 'uid', 'web' ),
48 * 'handlers' => array( 'stream' ),
49 * ),
50 * 'runJobs' => array(
51 * 'processors' => array( 'wiki', 'psr', 'pid' ),
52 * 'handlers' => array( 'stream' ),
53 * )
54 * ),
55 * 'processors' => array(
56 * 'wiki' => array(
57 * 'class' => '\\MediaWiki\\Logger\\Monolog\\WikiProcessor',
58 * ),
59 * 'psr' => array(
60 * 'class' => '\\Monolog\\Processor\\PsrLogMessageProcessor',
61 * ),
62 * 'pid' => array(
63 * 'class' => '\\Monolog\\Processor\\ProcessIdProcessor',
64 * ),
65 * 'uid' => array(
66 * 'class' => '\\Monolog\\Processor\\UidProcessor',
67 * ),
68 * 'web' => array(
69 * 'class' => '\\Monolog\\Processor\\WebProcessor',
70 * ),
71 * ),
72 * 'handlers' => array(
73 * 'stream' => array(
74 * 'class' => '\\Monolog\\Handler\\StreamHandler',
75 * 'args' => array( 'path/to/your.log' ),
76 * 'formatter' => 'line',
77 * ),
78 * 'redis' => array(
79 * 'class' => '\\Monolog\\Handler\\RedisHandler',
80 * 'args' => array( function() {
81 * $redis = new Redis();
82 * $redis->connect( '127.0.0.1', 6379 );
83 * return $redis;
84 * },
85 * 'logstash'
86 * ),
87 * 'formatter' => 'logstash',
88 * 'buffer' => true,
89 * ),
90 * 'udp2log' => array(
91 * 'class' => '\\MediaWiki\\Logger\\Monolog\\LegacyHandler',
92 * 'args' => array(
93 * 'udp://127.0.0.1:8420/mediawiki
94 * ),
95 * 'formatter' => 'line',
96 * ),
97 * ),
98 * 'formatters' => array(
99 * 'line' => array(
100 * 'class' => '\\Monolog\\Formatter\\LineFormatter',
101 * ),
102 * 'logstash' => array(
103 * 'class' => '\\Monolog\\Formatter\\LogstashFormatter',
104 * 'args' => array( 'mediawiki', php_uname( 'n' ), null, '', 1 ),
105 * ),
106 * ),
107 * ) ),
108 * );
109 * @endcode
110 *
111 * @see https://github.com/Seldaek/monolog
112 * @since 1.25
113 * @author Bryan Davis <bd808@wikimedia.org>
114 * @copyright © 2014 Bryan Davis and Wikimedia Foundation.
115 */
116 class MonologSpi implements Spi {
117
118 /**
119 * @var array $singletons
120 */
121 protected $singletons;
122
123 /**
124 * Configuration for creating new loggers.
125 * @var array $config
126 */
127 protected $config;
128
129
130 /**
131 * @param array $config Configuration data.
132 */
133 public function __construct( array $config ) {
134 $this->config = array();
135 $this->mergeConfig( $config );
136 }
137
138
139 /**
140 * Merge additional configuration data into the configuration.
141 *
142 * @since 1.26
143 * @param array $config Configuration data.
144 */
145 public function mergeConfig( array $config ) {
146 foreach ( $config as $key => $value ) {
147 if ( isset( $this->config[$key] ) ) {
148 $this->config[$key] = array_merge( $this->config[$key], $value );
149 } else {
150 $this->config[$key] = $value;
151 }
152 }
153 $this->reset();
154 }
155
156
157 /**
158 * Reset internal caches.
159 *
160 * This is public for use in unit tests. Under normal operation there should
161 * be no need to flush the caches.
162 */
163 public function reset() {
164 $this->singletons = array(
165 'loggers' => array(),
166 'handlers' => array(),
167 'formatters' => array(),
168 'processors' => array(),
169 );
170 }
171
172
173 /**
174 * Get a logger instance.
175 *
176 * Creates and caches a logger instance based on configuration found in the
177 * $wgMWLoggerMonologSpiConfig global. Subsequent request for the same channel
178 * name will return the cached instance.
179 *
180 * @param string $channel Logging channel
181 * @return \Psr\Log\LoggerInterface Logger instance
182 */
183 public function getLogger( $channel ) {
184 if ( !isset( $this->singletons['loggers'][$channel] ) ) {
185 // Fallback to using the '@default' configuration if an explict
186 // configuration for the requested channel isn't found.
187 $spec = isset( $this->config['loggers'][$channel] ) ?
188 $this->config['loggers'][$channel] :
189 $this->config['loggers']['@default'];
190
191 $monolog = $this->createLogger( $channel, $spec );
192 $this->singletons['loggers'][$channel] = $monolog;
193 }
194
195 return $this->singletons['loggers'][$channel];
196 }
197
198
199 /**
200 * Create a logger.
201 * @param string $channel Logger channel
202 * @param array $spec Configuration
203 * @return \Monolog\Logger
204 */
205 protected function createLogger( $channel, $spec ) {
206 $obj = new Logger( $channel );
207
208 if ( isset( $spec['calls'] ) ) {
209 foreach ( $spec['calls'] as $method => $margs ) {
210 call_user_func_array( array( $obj, $method ), $margs );
211 }
212 }
213
214 if ( isset( $spec['processors'] ) ) {
215 foreach ( $spec['processors'] as $processor ) {
216 $obj->pushProcessor( $this->getProcessor( $processor ) );
217 }
218 }
219
220 if ( isset( $spec['handlers'] ) ) {
221 foreach ( $spec['handlers'] as $handler ) {
222 $obj->pushHandler( $this->getHandler( $handler ) );
223 }
224 }
225 return $obj;
226 }
227
228
229 /**
230 * Create or return cached processor.
231 * @param string $name Processor name
232 * @return callable
233 */
234 public function getProcessor( $name ) {
235 if ( !isset( $this->singletons['processors'][$name] ) ) {
236 $spec = $this->config['processors'][$name];
237 $processor = ObjectFactory::getObjectFromSpec( $spec );
238 $this->singletons['processors'][$name] = $processor;
239 }
240 return $this->singletons['processors'][$name];
241 }
242
243
244 /**
245 * Create or return cached handler.
246 * @param string $name Processor name
247 * @return \Monolog\Handler\HandlerInterface
248 */
249 public function getHandler( $name ) {
250 if ( !isset( $this->singletons['handlers'][$name] ) ) {
251 $spec = $this->config['handlers'][$name];
252 $handler = ObjectFactory::getObjectFromSpec( $spec );
253 if ( isset( $spec['formatter'] ) ) {
254 $handler->setFormatter(
255 $this->getFormatter( $spec['formatter'] )
256 );
257 }
258 if ( isset( $spec['buffer'] ) && $spec['buffer'] ) {
259 $handler = new BufferHandler( $handler );
260 }
261 $this->singletons['handlers'][$name] = $handler;
262 }
263 return $this->singletons['handlers'][$name];
264 }
265
266
267 /**
268 * Create or return cached formatter.
269 * @param string $name Formatter name
270 * @return \Monolog\Formatter\FormatterInterface
271 */
272 public function getFormatter( $name ) {
273 if ( !isset( $this->singletons['formatters'][$name] ) ) {
274 $spec = $this->config['formatters'][$name];
275 $formatter = ObjectFactory::getObjectFromSpec( $spec );
276 $this->singletons['formatters'][$name] = $formatter;
277 }
278 return $this->singletons['formatters'][$name];
279 }
280 }