From: Philipp Holzer Date: Wed, 27 Feb 2019 15:40:35 +0000 (+0100) Subject: Adding SyslogLogger X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=9c5e0ae4155c69fc6d42e6c5678c2cb3a8a134da;p=friendica.git Adding SyslogLogger --- diff --git a/config/defaults.config.php b/config/defaults.config.php index a6f90f319e..e2aa13d342 100644 --- a/config/defaults.config.php +++ b/config/defaults.config.php @@ -214,6 +214,10 @@ return [ // If activated, all hashtags will point to the local server. 'local_tags' => false, + // logger_adapter (String) + // Sets the logging adapter of Friendica globally (monolog, syslog) + 'logger_adapter' => 'syslog', + // max_batch_queue (Integer) // Maximum number of batched queue items for a single contact before subsequent messages are discarded. 'max_batch_queue' => 1000, diff --git a/src/Factory/LoggerFactory.php b/src/Factory/LoggerFactory.php index 81c15bdb5b..863b30e57a 100644 --- a/src/Factory/LoggerFactory.php +++ b/src/Factory/LoggerFactory.php @@ -6,7 +6,8 @@ use Friendica\Core\Config\Configuration; use Friendica\Core\Logger; use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Util\Logger\FriendicaDevelopHandler; -use Friendica\Util\Logger\FriendicaIntrospectionProcessor; +use Friendica\Util\Logger\Introspection; +use Friendica\Util\Logger\SyslogLogger; use Friendica\Util\Logger\WorkerLogger; use Friendica\Util\Profiler; use Monolog; @@ -37,27 +38,39 @@ class LoggerFactory * @param Configuration $config The config * * @return LoggerInterface The PSR-3 compliant logger instance + * @throws InternalServerErrorException */ public static function create($channel, Configuration $config) { - $loggerTimeZone = new \DateTimeZone('UTC'); - Monolog\Logger::setTimezone($loggerTimeZone); - - $logger = new Monolog\Logger($channel); - $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); - $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); - $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, self::$ignoreClassList)); - - $debugging = $config->get('system', 'debugging'); - $stream = $config->get('system', 'logfile'); - $level = $config->get('system', 'loglevel'); - - if ($debugging) { - $loglevel = self::mapLegacyConfigDebugLevel((string)$level); - static::addStreamHandler($logger, $stream, $loglevel); - } else { - static::addVoidHandler($logger); + $introspector = new Introspection(LogLevel::DEBUG, self::$ignoreClassList); + switch ($config->get('system', 'logger_adapter', 'monolog')) { + case 'syslog': + $level = $config->get('system', 'loglevel'); + + $logger = new SyslogLogger($channel, $introspector, $level); + break; + case 'monolog': + default: + $loggerTimeZone = new \DateTimeZone('UTC'); + Monolog\Logger::setTimezone($loggerTimeZone); + + $logger = new Monolog\Logger($channel); + $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); + $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); + $logger->pushProcessor(new Monolog\Processor\UidProcessor()); + $logger->pushProcessor($introspector); + + $debugging = $config->get('system', 'debugging'); + $stream = $config->get('system', 'logfile'); + $level = $config->get('system', 'loglevel'); + + if ($debugging) { + $loglevel = self::mapLegacyConfigDebugLevel((string)$level); + static::addStreamHandler($logger, $stream, $loglevel); + } else { + static::addVoidHandler($logger); + } + break; } Logger::init($logger); @@ -77,6 +90,7 @@ class LoggerFactory * @param Configuration $config The config * * @return LoggerInterface The PSR-3 compliant logger instance + * @throws InternalServerErrorException */ public static function createDev($channel, Configuration $config) { @@ -95,7 +109,7 @@ class LoggerFactory $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor()); $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor()); $logger->pushProcessor(new Monolog\Processor\UidProcessor()); - $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, self::$ignoreClassList)); + $logger->pushProcessor(new Introspection(LogLevel::DEBUG, self::$ignoreClassList)); $logger->pushHandler(new FriendicaDevelopHandler($developerIp)); diff --git a/src/Util/Logger/FriendicaIntrospectionProcessor.php b/src/Util/Logger/FriendicaIntrospectionProcessor.php deleted file mode 100644 index aa3933a215..0000000000 --- a/src/Util/Logger/FriendicaIntrospectionProcessor.php +++ /dev/null @@ -1,94 +0,0 @@ -level = Logger::toMonologLevel($level); - $this->skipClassesPartials = array_merge(array('Monolog\\'), $skipClassesPartials); - $this->skipStackFramesCount = $skipStackFramesCount; - } - - public function __invoke(array $record) - { - // return if the level is not high enough - if ($record['level'] < $this->level) { - return $record; - } - - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - - $i = 1; - - while ($this->isTraceClassOrSkippedFunction($trace, $i)) { - $i++; - } - - $i += $this->skipStackFramesCount; - - // we should have the call source now - $record['extra'] = array_merge( - $record['extra'], - [ - 'file' => isset($trace[$i - 1]['file']) ? basename($trace[$i - 1]['file']) : null, - 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, - 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, - ] - ); - - return $record; - } - - /** - * Checks if the current trace class or function has to be skipped - * - * @param array $trace The current trace array - * @param int $index The index of the current hierarchy level - * @return bool True if the class or function should get skipped, otherwise false - */ - private function isTraceClassOrSkippedFunction(array $trace, $index) - { - if (!isset($trace[$index])) { - return false; - } - - if (isset($trace[$index]['class'])) { - foreach ($this->skipClassesPartials as $part) { - if (strpos($trace[$index]['class'], $part) !== false) { - return true; - } - } - } elseif (in_array($trace[$index]['function'], $this->skipFunctions)) { - return true; - } - - return false; - } -} diff --git a/src/Util/Logger/Introspection.php b/src/Util/Logger/Introspection.php new file mode 100644 index 0000000000..83fec6c50c --- /dev/null +++ b/src/Util/Logger/Introspection.php @@ -0,0 +1,103 @@ +level = Logger::toMonologLevel($level); + $this->skipClassesPartials = array_merge(array('Monolog\\'), $skipClassesPartials); + $this->skipStackFramesCount = $skipStackFramesCount; + } + + public function __invoke(array $record) + { + // return if the level is not high enough + if ($record['level'] < $this->level) { + return $record; + } + // we should have the call source now + $record['extra'] = array_merge( + $record['extra'], + $this->getRecord() + ); + + return $record; + } + + /** + * Returns the introspection record of the current call + * + * @return array + */ + public function getRecord() + { + $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); + + $i = 1; + + while ($this->isTraceClassOrSkippedFunction($trace, $i)) { + $i++; + } + + $i += $this->skipStackFramesCount; + + return [ + 'file' => isset($trace[$i - 1]['file']) ? basename($trace[$i - 1]['file']) : null, + 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, + 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null, + ]; + } + + /** + * Checks if the current trace class or function has to be skipped + * + * @param array $trace The current trace array + * @param int $index The index of the current hierarchy level + * @return bool True if the class or function should get skipped, otherwise false + */ + private function isTraceClassOrSkippedFunction(array $trace, $index) + { + if (!isset($trace[$index])) { + return false; + } + + if (isset($trace[$index]['class'])) { + foreach ($this->skipClassesPartials as $part) { + if (strpos($trace[$index]['class'], $part) !== false) { + return true; + } + } + } elseif (in_array($trace[$index]['function'], $this->skipFunctions)) { + return true; + } + + return false; + } +} diff --git a/src/Util/Logger/SyslogLogger.php b/src/Util/Logger/SyslogLogger.php new file mode 100644 index 0000000000..3734e688e9 --- /dev/null +++ b/src/Util/Logger/SyslogLogger.php @@ -0,0 +1,215 @@ + LOG_DEBUG, + LogLevel::INFO => LOG_INFO, + LogLevel::NOTICE => LOG_NOTICE, + LogLevel::WARNING => LOG_WARNING, + LogLevel::ERROR => LOG_ERR, + LogLevel::CRITICAL => LOG_CRIT, + LogLevel::ALERT => LOG_ALERT, + LogLevel::EMERGENCY => LOG_EMERG, + ]; + + /** + * The standard ident of the syslog (added to each message) + * @var string + */ + private $ident; + + /** + * Indicates what logging options will be used when generating a log message + * @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters + * + * @var int + */ + private $logOpts; + + /** + * Used to specify what type of program is logging the message + * @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters + * + * @var int + */ + private $logFacility; + + /** + * The minimum loglevel at which this logger will be triggered + * @var int + */ + private $logLevel; + + /** + * The Introspector for the current call + * @var Introspection + */ + private $introspection; + + /** + * @param string $channel The channel (Syslog ident) + * @param string $level The minimum loglevel at which this logger will be triggered + * @param int $logOpts Indicates what logging options will be used when generating a log message + * @param int $logFacility Used to specify what type of program is logging the message + * + * @throws InternalServerErrorException if the loglevel isn't valid + */ + public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER) + { + $this->ident = $channel; + $this->logOpts = $logOpts; + $this->logFacility = $logFacility; + $this->logLevel = $this->mapLevelToPriority($level); + $this->introspection = $introspection; + } + + /** + * Maps the LogLevel (@see LogLevel ) to a SysLog priority (@see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters ) + * + * @param string $level A LogLevel + * + * @return int The SysLog priority + * + * @throws \Psr\Log\InvalidArgumentException If the loglevel isn't valid + */ + public function mapLevelToPriority($level) + { + if (!array_key_exists($level, $this->logLevels)) { + throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.'); + } + + return $this->logLevels[$level]; + } + + /** + * Writes a message to the syslog + * + * @param int $priority The Priority ( @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters ) + * @param string $message The message of the log + * @throws InternalServerErrorException if syslog cannot be used + */ + private function write($priority, $message) + { + if (!openlog($this->ident, $this->logOpts, $this->logFacility)) { + throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->ident . '" and facility "' . $this->logFacility . '""'); + } + + syslog($priority, $message); + } + + public function close() + { + closelog(); + } + + private function formatLog($level, $message, $context = []) + { + $logMessage = ''; + + $logMessage .= $this->ident . ' '; + $logMessage .= '[' . $level . ']: '; + $logMessage .= $message . ' '; + $logMessage .= json_encode($context) . ' - '; + $logMessage .= json_encode($this->introspection->getRecord()); + + return $logMessage; + } + + private function addEntry($level, $message, $context = []) + { + if ($level >= $this->logLevel) { + return; + } + + $formattedLog = $this->formatLog($level, $message, $context); + $this->write($level, $formattedLog); + } + + /** + * {@inheritdoc} + */ + public function emergency($message, array $context = array()) + { + $this->addEntry(LOG_EMERG, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function alert($message, array $context = array()) + { + $this->addEntry(LOG_ALERT, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function critical($message, array $context = array()) + { + $this->addEntry(LOG_CRIT, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function error($message, array $context = array()) + { + $this->addEntry(LOG_ERR, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function warning($message, array $context = array()) + { + $this->addEntry(LOG_WARNING, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function notice($message, array $context = array()) + { + $this->addEntry(LOG_NOTICE, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function info($message, array $context = array()) + { + $this->addEntry(LOG_INFO, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function debug($message, array $context = array()) + { + $this->addEntry(LOG_DEBUG, $message, $context); + } + + /** + * {@inheritdoc} + */ + public function log($level, $message, array $context = array()) + { + $logLevel = $this->mapLevelToPriority($level); + $this->addEntry($logLevel, $message, $context); + } +}