]> git.mxchange.org Git - friendica.git/blob - src/Factory/LoggerFactory.php
Merge pull request #7988 from friendica/MrPetovan-notice
[friendica.git] / src / Factory / LoggerFactory.php
1 <?php
2
3 namespace Friendica\Factory;
4
5 use Friendica\Core\Config\Configuration;
6 use Friendica\Core\Logger;
7 use Friendica\Database\Database;
8 use Friendica\Network\HTTPException\InternalServerErrorException;
9 use Friendica\Util\FileSystem;
10 use Friendica\Util\Introspection;
11 use Friendica\Util\Logger\Monolog\DevelopHandler;
12 use Friendica\Util\Logger\Monolog\IntrospectionProcessor;
13 use Friendica\Util\Logger\ProfilerLogger;
14 use Friendica\Util\Logger\StreamLogger;
15 use Friendica\Util\Logger\SyslogLogger;
16 use Friendica\Util\Logger\VoidLogger;
17 use Friendica\Util\Profiler;
18 use Monolog;
19 use Psr\Log\LoggerInterface;
20 use Psr\Log\LogLevel;
21
22 /**
23  * A logger factory
24  *
25  * Currently only Monolog is supported
26  */
27 class LoggerFactory
28 {
29         const DEV_CHANNEL = 'dev';
30
31         /**
32          * A list of classes, which shouldn't get logged
33          *
34          * @var array
35          */
36         private static $ignoreClassList = [
37                 Logger::class,
38                 Profiler::class,
39                 'Friendica\\Util\\Logger',
40         ];
41
42         private $channel;
43
44         public function __construct(string $channel)
45         {
46                 $this->channel = $channel;
47         }
48
49         /**
50          * Creates a new PSR-3 compliant logger instances
51          *
52          * @param Database      $database The Friendica Database instance
53          * @param Configuration $config   The config
54          * @param Profiler      $profiler The profiler of the app
55          * @param FileSystem    $fileSystem FileSystem utils
56          *
57          * @return LoggerInterface The PSR-3 compliant logger instance
58          */
59         public function create(Database $database, Configuration $config, Profiler $profiler, FileSystem $fileSystem)
60         {
61                 if (empty($config->get('system', 'debugging', false))) {
62                         $logger = new VoidLogger();
63                         $database->setLogger($logger);
64                         return $logger;
65                 }
66
67                 $introspection = new Introspection(self::$ignoreClassList);
68                 $level         = $config->get('system', 'loglevel');
69                 $loglevel      = self::mapLegacyConfigDebugLevel((string)$level);
70
71                 switch ($config->get('system', 'logger_config', 'stream')) {
72                         case 'monolog':
73                                 $loggerTimeZone = new \DateTimeZone('UTC');
74                                 Monolog\Logger::setTimezone($loggerTimeZone);
75
76                                 $logger = new Monolog\Logger($this->channel);
77                                 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
78                                 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
79                                 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
80                                 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
81
82                                 $stream = $config->get('system', 'logfile');
83
84                                 // just add a stream in case it's either writable or not file
85                                 if (!is_file($stream) || is_writable($stream)) {
86                                         try {
87                                                 static::addStreamHandler($logger, $stream, $loglevel);
88                                         } catch (\Throwable $e) {
89                                                 // No Logger ..
90                                                 $logger = new VoidLogger();
91                                         }
92                                 }
93                                 break;
94
95                         case 'syslog':
96                                 try {
97                                         $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
98                                 } catch (\Throwable $e) {
99                                         // No logger ...
100                                         $logger = new VoidLogger();
101                                 }
102                                 break;
103
104                         case 'stream':
105                         default:
106                                 $stream = $config->get('system', 'logfile');
107                                 // just add a stream in case it's either writable or not file
108                                 if (!is_file($stream) || is_writable($stream)) {
109                                         try {
110                                                 $logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel);
111                                         } catch (\Throwable $t) {
112                                                 // No logger ...
113                                                 $logger = new VoidLogger();
114                                         }
115                                 } else {
116                                         $logger = new VoidLogger();
117                                 }
118                                 break;
119                 }
120
121                 $profiling = $config->get('system', 'profiling', false);
122
123                 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
124                 if (isset($profiling) && $profiling !== false) {
125                         $logger = new ProfilerLogger($logger, $profiler);
126                 }
127
128                 $database->setLogger($logger);
129                 return $logger;
130         }
131
132         /**
133          * Creates a new PSR-3 compliant develop logger
134          *
135          * If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
136          * you'll use this logger instance for the duration of your work.
137          *
138          * It should never get filled during normal usage of Friendica
139          *
140          * @param Configuration $config   The config
141          * @param Profiler      $profiler The profiler of the app
142          * @param FileSystem    $fileSystem FileSystem utils
143          *
144          * @return LoggerInterface The PSR-3 compliant logger instance
145          *
146          * @throws InternalServerErrorException
147          * @throws \Exception
148          */
149         public static function createDev(Configuration $config, Profiler $profiler, FileSystem $fileSystem)
150         {
151                 $debugging   = $config->get('system', 'debugging');
152                 $stream      = $config->get('system', 'dlogfile');
153                 $developerIp = $config->get('system', 'dlogip');
154
155                 if ((!isset($developerIp) || !$debugging) &&
156                     (!is_file($stream) || is_writable($stream))) {
157                         $logger = new VoidLogger();
158                         return $logger;
159                 }
160
161                 $loggerTimeZone = new \DateTimeZone('UTC');
162                 Monolog\Logger::setTimezone($loggerTimeZone);
163
164                 $introspection = new Introspection(self::$ignoreClassList);
165
166                 switch ($config->get('system', 'logger_config', 'stream')) {
167
168                         case 'monolog':
169                                 $loggerTimeZone = new \DateTimeZone('UTC');
170                                 Monolog\Logger::setTimezone($loggerTimeZone);
171
172                                 $logger = new Monolog\Logger(self::DEV_CHANNEL);
173                                 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
174                                 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
175                                 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
176                                 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
177
178                                 $logger->pushHandler(new DevelopHandler($developerIp));
179
180                                 static::addStreamHandler($logger, $stream, LogLevel::DEBUG);
181                                 break;
182
183                         case 'syslog':
184                                 $logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG);
185                                 break;
186
187                         case 'stream':
188                         default:
189                                 $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, $fileSystem, LogLevel::DEBUG);
190                                 break;
191                 }
192
193                 $profiling = $config->get('system', 'profiling', false);
194
195                 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
196                 if (isset($profiling) && $profiling !== false) {
197                         $logger = new ProfilerLogger($logger, $profiler);
198                 }
199
200                 return $logger;
201         }
202
203         /**
204          * Mapping a legacy level to the PSR-3 compliant levels
205          *
206          * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
207          *
208          * @param string $level the level to be mapped
209          *
210          * @return string the PSR-3 compliant level
211          */
212         private static function mapLegacyConfigDebugLevel($level)
213         {
214                 switch ($level) {
215                         // legacy WARNING
216                         case "0":
217                                 return LogLevel::ERROR;
218                         // legacy INFO
219                         case "1":
220                                 return LogLevel::WARNING;
221                         // legacy TRACE
222                         case "2":
223                                 return LogLevel::NOTICE;
224                         // legacy DEBUG
225                         case "3":
226                                 return LogLevel::INFO;
227                         // legacy DATA
228                         case "4":
229                         // legacy ALL
230                         case "5":
231                                 return LogLevel::DEBUG;
232                         // default if nothing set
233                         default:
234                                 return $level;
235                 }
236         }
237
238         /**
239          * Adding a handler to a given logger instance
240          *
241          * @param LoggerInterface $logger The logger instance
242          * @param mixed           $stream The stream which handles the logger output
243          * @param string          $level  The level, for which this handler at least should handle logging
244          *
245          * @return void
246          *
247          * @throws \Exception in case of general failures
248          */
249         public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE)
250         {
251                 if ($logger instanceof Monolog\Logger) {
252                         $loglevel = Monolog\Logger::toMonologLevel($level);
253
254                         // fallback to notice if an invalid loglevel is set
255                         if (!is_int($loglevel)) {
256                                 $loglevel = LogLevel::NOTICE;
257                         }
258
259                         $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
260
261                         $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
262                         $fileHandler->setFormatter($formatter);
263
264                         $logger->pushHandler($fileHandler);
265                 }
266         }
267
268         public static function addVoidHandler($logger)
269         {
270                 if ($logger instanceof Monolog\Logger) {
271                         $logger->pushHandler(new Monolog\Handler\NullHandler());
272                 }
273         }
274 }