3 namespace Friendica\Factory;
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\Introspection;
10 use Friendica\Util\Logger\Monolog\DevelopHandler;
11 use Friendica\Util\Logger\Monolog\IntrospectionProcessor;
12 use Friendica\Util\Logger\ProfilerLogger;
13 use Friendica\Util\Logger\StreamLogger;
14 use Friendica\Util\Logger\SyslogLogger;
15 use Friendica\Util\Logger\VoidLogger;
16 use Friendica\Util\Profiler;
18 use Psr\Log\LoggerInterface;
24 * Currently only Monolog is supported
28 const DEV_CHANNEL = 'dev';
31 * A list of classes, which shouldn't get logged
35 private static $ignoreClassList = [
38 'Friendica\\Util\\Logger',
43 public function __construct(string $channel)
45 $this->channel = $channel;
49 * Creates a new PSR-3 compliant logger instances
51 * @param Database $database The Friendica Database instance
52 * @param Configuration $config The config
53 * @param Profiler $profiler The profiler of the app
55 * @return LoggerInterface The PSR-3 compliant logger instance
57 public function create( Database $database, Configuration $config, Profiler $profiler)
59 if (empty($config->get('system', 'debugging', false))) {
60 $logger = new VoidLogger();
61 $database->setLogger($logger);
65 $introspection = new Introspection(self::$ignoreClassList);
66 $level = $config->get('system', 'loglevel');
67 $loglevel = self::mapLegacyConfigDebugLevel((string)$level);
69 switch ($config->get('system', 'logger_config', 'stream')) {
71 $loggerTimeZone = new \DateTimeZone('UTC');
72 Monolog\Logger::setTimezone($loggerTimeZone);
74 $logger = new Monolog\Logger($this->channel);
75 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
76 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
77 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
78 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
80 $stream = $config->get('system', 'logfile');
82 // just add a stream in case it's either writable or not file
83 if (!is_file($stream) || is_writable($stream)) {
85 static::addStreamHandler($logger, $stream, $loglevel);
86 } catch (\Throwable $e) {
88 $logger = new VoidLogger();
95 $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
96 } catch (\Throwable $e) {
98 $logger = new VoidLogger();
104 $stream = $config->get('system', 'logfile');
105 // just add a stream in case it's either writable or not file
106 if (!is_file($stream) || is_writable($stream)) {
108 $logger = new StreamLogger($this->channel, $stream, $introspection, $loglevel);
109 } catch (\Throwable $t) {
111 $logger = new VoidLogger();
114 $logger = new VoidLogger();
119 $profiling = $config->get('system', 'profiling', false);
121 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
122 if (isset($profiling) && $profiling !== false) {
123 $logger = new ProfilerLogger($logger, $profiler);
126 $database->setLogger($logger);
131 * Creates a new PSR-3 compliant develop logger
133 * If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
134 * you'll use this logger instance for the duration of your work.
136 * It should never get filled during normal usage of Friendica
138 * @param Configuration $config The config
139 * @param Profiler $profiler The profiler of the app
141 * @return LoggerInterface The PSR-3 compliant logger instance
143 * @throws InternalServerErrorException
146 public static function createDev(Configuration $config, Profiler $profiler)
148 $debugging = $config->get('system', 'debugging');
149 $stream = $config->get('system', 'dlogfile');
150 $developerIp = $config->get('system', 'dlogip');
152 if ((!isset($developerIp) || !$debugging) &&
153 (!is_file($stream) || is_writable($stream))) {
154 $logger = new VoidLogger();
158 $loggerTimeZone = new \DateTimeZone('UTC');
159 Monolog\Logger::setTimezone($loggerTimeZone);
161 $introspection = new Introspection(self::$ignoreClassList);
163 switch ($config->get('system', 'logger_config', 'stream')) {
166 $loggerTimeZone = new \DateTimeZone('UTC');
167 Monolog\Logger::setTimezone($loggerTimeZone);
169 $logger = new Monolog\Logger(self::DEV_CHANNEL);
170 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
171 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
172 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
173 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
175 $logger->pushHandler(new DevelopHandler($developerIp));
177 static::addStreamHandler($logger, $stream, LogLevel::DEBUG);
181 $logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG);
186 $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, LogLevel::DEBUG);
190 $profiling = $config->get('system', 'profiling', false);
192 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
193 if (isset($profiling) && $profiling !== false) {
194 $logger = new ProfilerLogger($logger, $profiler);
201 * Mapping a legacy level to the PSR-3 compliant levels
203 * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
205 * @param string $level the level to be mapped
207 * @return string the PSR-3 compliant level
209 private static function mapLegacyConfigDebugLevel($level)
214 return LogLevel::ERROR;
217 return LogLevel::WARNING;
220 return LogLevel::NOTICE;
223 return LogLevel::INFO;
228 return LogLevel::DEBUG;
229 // default if nothing set
236 * Adding a handler to a given logger instance
238 * @param LoggerInterface $logger The logger instance
239 * @param mixed $stream The stream which handles the logger output
240 * @param string $level The level, for which this handler at least should handle logging
244 * @throws \Exception in case of general failures
246 public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE)
248 if ($logger instanceof Monolog\Logger) {
249 $loglevel = Monolog\Logger::toMonologLevel($level);
251 // fallback to notice if an invalid loglevel is set
252 if (!is_int($loglevel)) {
253 $loglevel = LogLevel::NOTICE;
256 $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
258 $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
259 $fileHandler->setFormatter($formatter);
261 $logger->pushHandler($fileHandler);
265 public static function addVoidHandler($logger)
267 if ($logger instanceof Monolog\Logger) {
268 $logger->pushHandler(new Monolog\Handler\NullHandler());