Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

264 lines
7.6KB

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