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