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
58 * @throws InternalServerErrorException
60 public function create( Database $database, Configuration $config, Profiler $profiler)
62 if (empty($config->get('system', 'debugging', false))) {
63 $logger = new VoidLogger();
64 $database->setLogger($logger);
68 $introspection = new Introspection(self::$ignoreClassList);
69 $level = $config->get('system', 'loglevel');
70 $loglevel = self::mapLegacyConfigDebugLevel((string)$level);
72 switch ($config->get('system', 'logger_config', 'stream')) {
74 $loggerTimeZone = new \DateTimeZone('UTC');
75 Monolog\Logger::setTimezone($loggerTimeZone);
77 $logger = new Monolog\Logger($this->channel);
78 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
79 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
80 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
81 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
83 $stream = $config->get('system', 'logfile');
85 // just add a stream in case it's either writable or not file
86 if (!is_file($stream) || is_writable($stream)) {
87 static::addStreamHandler($logger, $stream, $loglevel);
92 $logger = new SyslogLogger($this->channel, $introspection, $loglevel);
97 $stream = $config->get('system', 'logfile');
98 // just add a stream in case it's either writable or not file
99 if (!is_file($stream) || is_writable($stream)) {
100 $logger = new StreamLogger($this->channel, $stream, $introspection, $loglevel);
102 $logger = new VoidLogger();
107 $profiling = $config->get('system', 'profiling', false);
109 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
110 if (isset($profiling) && $profiling !== false) {
111 $logger = new ProfilerLogger($logger, $profiler);
114 $database->setLogger($logger);
119 * Creates a new PSR-3 compliant develop logger
121 * If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
122 * you'll use this logger instance for the duration of your work.
124 * It should never get filled during normal usage of Friendica
126 * @param Configuration $config The config
127 * @param Profiler $profiler The profiler of the app
129 * @return LoggerInterface The PSR-3 compliant logger instance
131 * @throws InternalServerErrorException
134 public static function createDev(Configuration $config, Profiler $profiler)
136 $debugging = $config->get('system', 'debugging');
137 $stream = $config->get('system', 'dlogfile');
138 $developerIp = $config->get('system', 'dlogip');
140 if ((!isset($developerIp) || !$debugging) &&
141 (!is_file($stream) || is_writable($stream))) {
142 $logger = new VoidLogger();
146 $loggerTimeZone = new \DateTimeZone('UTC');
147 Monolog\Logger::setTimezone($loggerTimeZone);
149 $introspection = new Introspection(self::$ignoreClassList);
151 switch ($config->get('system', 'logger_config', 'stream')) {
154 $loggerTimeZone = new \DateTimeZone('UTC');
155 Monolog\Logger::setTimezone($loggerTimeZone);
157 $logger = new Monolog\Logger(self::DEV_CHANNEL);
158 $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
159 $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
160 $logger->pushProcessor(new Monolog\Processor\UidProcessor());
161 $logger->pushProcessor(new IntrospectionProcessor($introspection, LogLevel::DEBUG));
163 $logger->pushHandler(new DevelopHandler($developerIp));
165 static::addStreamHandler($logger, $stream, LogLevel::DEBUG);
169 $logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG);
174 $logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, LogLevel::DEBUG);
178 $profiling = $config->get('system', 'profiling', false);
180 // In case profiling is enabled, wrap the ProfilerLogger around the current logger
181 if (isset($profiling) && $profiling !== false) {
182 $logger = new ProfilerLogger($logger, $profiler);
189 * Mapping a legacy level to the PSR-3 compliant levels
191 * @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
193 * @param string $level the level to be mapped
195 * @return string the PSR-3 compliant level
197 private static function mapLegacyConfigDebugLevel($level)
202 return LogLevel::ERROR;
205 return LogLevel::WARNING;
208 return LogLevel::NOTICE;
211 return LogLevel::INFO;
214 return LogLevel::DEBUG;
217 return LogLevel::DEBUG;
218 // default if nothing set
225 * Adding a handler to a given logger instance
227 * @param LoggerInterface $logger The logger instance
228 * @param mixed $stream The stream which handles the logger output
229 * @param string $level The level, for which this handler at least should handle logging
233 * @throws InternalServerErrorException if the logger is incompatible to the logger factory
234 * @throws \Exception in case of general failures
236 public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE)
238 if ($logger instanceof Monolog\Logger) {
239 $loglevel = Monolog\Logger::toMonologLevel($level);
241 // fallback to notice if an invalid loglevel is set
242 if (!is_int($loglevel)) {
243 $loglevel = LogLevel::NOTICE;
246 $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
248 $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
249 $fileHandler->setFormatter($formatter);
251 $logger->pushHandler($fileHandler);
253 throw new InternalServerErrorException('Logger instance incompatible for MonologFactory');
257 public static function addVoidHandler($logger)
259 if ($logger instanceof Monolog\Logger) {
260 $logger->pushHandler(new Monolog\Handler\NullHandler());