1<?php
2
3use Psr\Container\ContainerInterface;
4use Monolog\Logger;
5use Piwik\Log;
6use Piwik\Plugins\Monolog\Handler\FileHandler;
7use Piwik\Plugins\Monolog\Handler\LogCaptureHandler;
8
9return array(
10
11    'Monolog\Logger' => DI\create('Monolog\Logger')
12        ->constructor('piwik', DI\get('log.handlers'), DI\get('log.processors')),
13
14    'Psr\Log\LoggerInterface' => DI\get('Monolog\Logger'),
15
16    'log.handler.classes' => array(
17        'file'     => 'Piwik\Plugins\Monolog\Handler\FileHandler',
18        'screen'   => 'Piwik\Plugins\Monolog\Handler\WebNotificationHandler',
19        'database' => 'Piwik\Plugins\Monolog\Handler\DatabaseHandler',
20        'errorlog' => '\Monolog\Handler\ErrorLogHandler',
21        'syslog' => '\Monolog\Handler\SyslogHandler',
22    ),
23    'log.handlers' => DI\factory(function (\DI\Container $c) {
24        if ($c->has('ini.log.log_writers')) {
25            $writerNames = $c->get('ini.log.log_writers');
26        } else {
27            return array();
28        }
29
30        $classes = $c->get('log.handler.classes');
31
32        $logConfig = $c->get(\Piwik\Config::class)->log;
33        $enableFingersCrossed = isset($logConfig['enable_fingers_crossed_handler']) && $logConfig['enable_fingers_crossed_handler'] == 1;
34        $fingersCrossedStopBuffering = isset($logConfig['fingers_crossed_stop_buffering_on_activation']) && $logConfig['fingers_crossed_stop_buffering_on_activation'] == 1;
35        $enableLogCaptureHandler = isset($logConfig['enable_log_capture_handler']) && $logConfig['enable_log_capture_handler'] == 1;
36
37        $isLogBufferingAllowed = !\Piwik\Common::isPhpCliMode()
38            || \Piwik\SettingsServer::isArchivePhpTriggered()
39            || \Piwik\CliMulti::isCliMultiRequest();
40
41        $writerNames = array_map('trim', $writerNames);
42
43        $writers = [];
44        foreach ($writerNames as $writerName) {
45            if ($writerName === 'screen'
46                && \Piwik\Common::isPhpCliMode()
47                && !defined('PIWIK_TEST_MODE')
48                && !\Piwik\SettingsServer::isTrackerApiRequest()
49            ) {
50                continue; // screen writer is only valid for web requests (except for tracker CLI requests)
51            }
52
53            if (isset($classes[$writerName])) {
54                // wrap the handler in FingersCrossedHandler if we can and this isn't the screen handler
55
56                /** @var \Monolog\Handler\HandlerInterface $handler */
57                $handler = $c->make($classes[$writerName]);
58                if ($enableFingersCrossed
59                    && $writerName !== 'screen'
60                    && $handler instanceof \Monolog\Handler\AbstractHandler
61                    && $isLogBufferingAllowed
62                ) {
63                    $passthruLevel = $handler->getLevel();
64
65                    $handler->setLevel(Logger::DEBUG);
66
67                    $handler = new \Monolog\Handler\FingersCrossedHandler($handler, $activationStrategy = null, $bufferSize = 0,
68                        $bubble = true, $fingersCrossedStopBuffering, $passthruLevel);
69                }
70
71                $writers[$writerName] = $handler;
72            }
73        }
74
75        if ($enableLogCaptureHandler
76            && $isLogBufferingAllowed
77        ) {
78            $writers[] = $c->get(LogCaptureHandler::class);
79        }
80
81        // we always add the null handler to make sure there is at least one handler specified. otherwise Monolog will
82        // add a stream handler to stderr w/ a DEBUG log level, which will cause archiving requests to fail.
83        if (empty($writers)) {
84            $writers[] = $c->get(\Monolog\Handler\NullHandler::class);
85        }
86
87        return array_values($writers);
88    }),
89
90    'log.processors' => array(
91        DI\get('Piwik\Plugins\Monolog\Processor\SprintfProcessor'),
92        DI\get('Piwik\Plugins\Monolog\Processor\ClassNameProcessor'),
93        DI\get('Piwik\Plugins\Monolog\Processor\RequestIdProcessor'),
94        DI\get('Piwik\Plugins\Monolog\Processor\ExceptionToTextProcessor'),
95        DI\get('Monolog\Processor\PsrLogMessageProcessor'),
96        DI\get('Piwik\Plugins\Monolog\Processor\TokenProcessor'),
97    ),
98
99    'Piwik\Plugins\Monolog\Handler\FileHandler' => DI\create()
100        ->constructor(DI\get('log.file.filename'), DI\get('log.level.file'))
101        ->method('setFormatter', DI\get('log.lineMessageFormatter.file')),
102
103    '\Monolog\Handler\ErrorLogHandler' => DI\autowire()
104        ->constructorParameter('level', DI\get('log.level.errorlog'))
105        ->method('setFormatter', DI\get('log.lineMessageFormatter.file')),
106
107    '\Monolog\Handler\SyslogHandler' => DI\autowire()
108        ->constructorParameter('ident', DI\get('log.syslog.ident'))
109        ->constructorParameter('level', DI\get('log.level.syslog'))
110        ->method('setFormatter', DI\get('log.lineMessageFormatter.file')),
111
112    'Piwik\Plugins\Monolog\Handler\DatabaseHandler' => DI\create()
113        ->constructor(DI\get('log.level.database'))
114        ->method('setFormatter', DI\get('log.lineMessageFormatter')),
115
116    'Piwik\Plugins\Monolog\Handler\WebNotificationHandler' => DI\create()
117        ->constructor(DI\get('log.level.screen'))
118        ->method('setFormatter', DI\get('log.lineMessageFormatter')),
119
120    'log.level' => DI\factory(function (ContainerInterface $c) {
121        if ($c->has('ini.log.log_level')) {
122            $level = strtoupper($c->get('ini.log.log_level'));
123            if (!empty($level) && defined('Piwik\Log::'.strtoupper($level))) {
124                return Log::getMonologLevel(constant('Piwik\Log::'.strtoupper($level)));
125            }
126        }
127
128        return Logger::WARNING;
129    }),
130
131    'log.level.file' => DI\factory(function (ContainerInterface $c) {
132        if ($c->has('ini.log.log_level_file')) {
133            $level = Log::getMonologLevelIfValid($c->get('ini.log.log_level_file'));
134            if ($level !== null) {
135                return $level;
136            }
137        }
138        return $c->get('log.level');
139    }),
140
141    'log.level.screen' => DI\factory(function (ContainerInterface $c) {
142        if ($c->has('ini.log.log_level_screen')) {
143            $level = Log::getMonologLevelIfValid($c->get('ini.log.log_level_screen'));
144            if ($level !== null) {
145                return $level;
146            }
147        }
148        return $c->get('log.level');
149    }),
150
151    'log.level.database' => DI\factory(function (ContainerInterface $c) {
152        if ($c->has('ini.log.log_level_database')) {
153            $level = Log::getMonologLevelIfValid($c->get('ini.log.log_level_database'));
154            if ($level !== null) {
155                return $level;
156            }
157        }
158        return $c->get('log.level');
159    }),
160
161    'log.level.syslog' => DI\factory(function (ContainerInterface $c) {
162        if ($c->has('ini.log.log_level_syslog')) {
163            $level = Log::getMonologLevelIfValid($c->get('ini.log.log_level_syslog'));
164            if ($level !== null) {
165                return $level;
166            }
167        }
168        return $c->get('log.level');
169    }),
170
171    'log.level.errorlog' => DI\factory(function (ContainerInterface $c) {
172        if ($c->has('ini.log.log_level_errorlog')) {
173            $level = Log::getMonologLevelIfValid($c->get('ini.log.log_level_errorlog'));
174            if ($level !== null) {
175                return $level;
176            }
177        }
178        return $c->get('log.level');
179    }),
180
181    'log.file.filename' => DI\factory(function (ContainerInterface $c) {
182        $logPath = $c->get('ini.log.logger_file_path');
183
184        // Absolute path
185        if (strpos($logPath, '/') === 0) {
186            return $logPath;
187        }
188
189        // Remove 'tmp/' at the beginning
190        if (strpos($logPath, 'tmp/') === 0) {
191            $logPath = substr($logPath, strlen('tmp'));
192        }
193
194        if (empty($logPath)) {
195            // Default log file
196            $logPath = '/logs/piwik.log';
197        }
198
199        $logPath = $c->get('path.tmp') . $logPath;
200        if (is_dir($logPath)) {
201            $logPath .= '/piwik.log';
202        }
203
204        return $logPath;
205    }),
206
207    'log.syslog.ident' => DI\factory(function (ContainerInterface $c) {
208        $ident = $c->get('ini.log.logger_syslog_ident');
209        if (empty($ident)) {
210            $ident = 'matomo';
211        }
212        return $ident;
213    }),
214
215    'Piwik\Plugins\Monolog\Formatter\LineMessageFormatter' => DI\create('Piwik\Plugins\Monolog\Formatter\LineMessageFormatter')
216                                                                ->constructor(DI\get('log.short.format')),
217    'log.lineMessageFormatter' => DI\create('Piwik\Plugins\Monolog\Formatter\LineMessageFormatter')
218        ->constructor(DI\get('log.short.format')),
219
220    'log.lineMessageFormatter.file' => DI\autowire('Piwik\Plugins\Monolog\Formatter\LineMessageFormatter')
221        ->constructor(DI\get('log.trace.format'))
222        ->constructorParameter('allowInlineLineBreaks', false),
223
224    'log.short.format' => DI\factory(function (ContainerInterface $c) {
225        if ($c->has('ini.log.string_message_format')) {
226            return $c->get('ini.log.string_message_format');
227        }
228        return '%level% %tag%[%datetime%] %message%';
229    }),
230
231    'log.trace.format' => DI\factory(function (ContainerInterface $c) {
232        if ($c->has('ini.log.string_message_format_trace')) {
233            return $c->get('ini.log.string_message_format_trace');
234        }
235        return '%level% %tag%[%datetime%] %message% %trace%';
236    }),
237
238    'archiving.performance.handlers' => function (ContainerInterface $c) {
239        $logFile = trim($c->get('ini.Debug.archive_profiling_log'));
240        if (empty($logFile)) {
241            return [new \Monolog\Handler\NullHandler()];
242        }
243
244        $fileHandler = new FileHandler($logFile, \Psr\Log\LogLevel::INFO);
245        $fileHandler->setFormatter($c->get('log.lineMessageFormatter.file'));
246        return [$fileHandler];
247    },
248
249    'archiving.performance.logger' => DI\create(Logger::class)
250        ->constructor('matomo.archiving.performance', DI\get('archiving.performance.handlers'), DI\get('log.processors')),
251);
252