X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FUtil%2FLogger%2FSyslogLogger.php;h=83c3fc3ce57e8707ebc4891fb7e564fba199eb65;hb=3940e804e3ee4ac921e109f62a73fac2becaa611;hp=994c0a1e24b87f493dab060d06e7e26676b213ae;hpb=ae4096350792b312ea20621456a65d306cb22e87;p=friendica.git diff --git a/src/Util/Logger/SyslogLogger.php b/src/Util/Logger/SyslogLogger.php index 994c0a1e24..83c3fc3ce5 100644 --- a/src/Util/Logger/SyslogLogger.php +++ b/src/Util/Logger/SyslogLogger.php @@ -3,37 +3,46 @@ namespace Friendica\Util\Logger; use Friendica\Network\HTTPException\InternalServerErrorException; -use Psr\Log\InvalidArgumentException; -use Psr\Log\LoggerInterface; +use Friendica\Util\Introspection; use Psr\Log\LogLevel; /** * A Logger instance for syslogging (fast, but simple) * @see http://php.net/manual/en/function.syslog.php */ -class SyslogLogger implements LoggerInterface +class SyslogLogger extends AbstractLogger { const IDENT = 'Friendica'; /** * Translates LogLevel log levels to syslog log priorities. + * @var array */ private $logLevels = [ - LogLevel::DEBUG => 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::DEBUG => 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 channel of the current process (added to each message) - * @var string + * Translates log priorities to string outputs + * @var array */ - private $channel; + private $logToString = [ + LOG_DEBUG => 'DEBUG', + LOG_INFO => 'INFO', + LOG_NOTICE => 'NOTICE', + LOG_WARNING => 'WARNING', + LOG_ERR => 'ERROR', + LOG_CRIT => 'CRITICAL', + LOG_ALERT => 'ALERT', + LOG_EMERG => 'EMERGENCY' + ]; /** * Indicates what logging options will be used when generating a log message @@ -58,24 +67,47 @@ class SyslogLogger implements LoggerInterface private $logLevel; /** - * The Introspector for the current call - * @var Introspection + * A error message of the current operation + * @var string */ - private $introspection; + private $errorMessage; /** - * @param string $channel The output channel + * {@inheritdoc} * @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 \Exception */ public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER) { - $this->channel = $channel; + parent::__construct($channel, $introspection); $this->logOpts = $logOpts; $this->logFacility = $logFacility; $this->logLevel = $this->mapLevelToPriority($level); - $this->introspection = $introspection; + $this->introspection->addClasses(array(self::class)); + } + + /** + * Adds a new entry to the syslog + * + * @param int $level + * @param string $message + * @param array $context + * + * @throws InternalServerErrorException if the syslog isn't available + */ + protected function addEntry($level, $message, $context = []) + { + $logLevel = $this->mapLevelToPriority($level); + + if ($logLevel > $this->logLevel) { + return; + } + + $formattedLog = $this->formatLog($logLevel, $message, $context); + $this->write($logLevel, $formattedLog); } /** @@ -90,12 +122,20 @@ class SyslogLogger implements LoggerInterface public function mapLevelToPriority($level) { if (!array_key_exists($level, $this->logLevels)) { - throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.'); + throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); } return $this->logLevels[$level]; } + /** + * Closes the Syslog + */ + public function close() + { + closelog(); + } + /** * Writes a message to the syslog * @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters @@ -107,19 +147,15 @@ class SyslogLogger implements LoggerInterface */ private function write($priority, $message) { - if (!openlog(self::IDENT, $this->logOpts, $this->logFacility)) { - throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->channel . '" and facility "' . $this->logFacility . '""'); + set_error_handler([$this, 'customErrorHandler']); + $opened = openlog(self::IDENT, $this->logOpts, $this->logFacility); + restore_error_handler(); + + if (!$opened) { + throw new \UnexpectedValueException(sprintf('Can\'t open syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility)); } - syslog($priority, $message); - } - - /** - * Closes the Syslog - */ - public function close() - { - closelog(); + $this->syslogWrapper($priority, $message); } /** @@ -133,139 +169,38 @@ class SyslogLogger implements LoggerInterface */ private function formatLog($level, $message, $context = []) { + $record = $this->introspection->getRecord(); + $record = array_merge($record, ['uid' => $this->logUid]); $logMessage = ''; $logMessage .= $this->channel . ' '; - $logMessage .= '[' . $level . ']: '; + $logMessage .= '[' . $this->logToString[$level] . ']: '; $logMessage .= $this->psrInterpolate($message, $context) . ' '; $logMessage .= @json_encode($context) . ' - '; - $logMessage .= @json_encode($this->introspection->getRecord()); + $logMessage .= @json_encode($record); return $logMessage; } - /** - * Simple interpolation of PSR-3 compliant replacements ( between '{' and '}' ) - * @see https://www.php-fig.org/psr/psr-3/#12-message - * - * @param string $message - * @param array $context - * - * @return string the interpolated message - */ - private function psrInterpolate($message, array $context = array()) + private function customErrorHandler($code, $msg) { - $replace = []; - foreach ($context as $key => $value) { - // check that the value can be casted to string - if (!is_array($value) && (!is_object($value) || method_exists($value, '__toString'))) { - $replace['{' . $key . '}'] = $value; - } elseif (is_array($value)) { - $replace['{' . $key . '}'] = @json_encode($value); - } - } - - return strtr($message, $replace); + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); } /** - * Adds a new entry to the syslog - * - * @param int $level - * @param string $message - * @param array $context + * A syslog wrapper to make syslog functionality testable * - * @throws InternalServerErrorException if the syslog isn't available - */ - private function addEntry($level, $message, $context = []) - { - if ($level >= $this->logLevel) { - return; - } - - $formattedLog = $this->formatLog($level, $message, $context); - $this->write($level, $formattedLog); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function emergency($message, array $context = array()) - { - $this->addEntry(LOG_EMERG, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function alert($message, array $context = array()) - { - $this->addEntry(LOG_ALERT, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function critical($message, array $context = array()) - { - $this->addEntry(LOG_CRIT, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function error($message, array $context = array()) - { - $this->addEntry(LOG_ERR, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function warning($message, array $context = array()) - { - $this->addEntry(LOG_WARNING, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function notice($message, array $context = array()) - { - $this->addEntry(LOG_NOTICE, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available + * @param int $level The syslog priority + * @param string $entry The message to send to the syslog function */ - public function info($message, array $context = array()) + protected function syslogWrapper($level, $entry) { - $this->addEntry(LOG_INFO, $message, $context); - } - - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function debug($message, array $context = array()) - { - $this->addEntry(LOG_DEBUG, $message, $context); - } + set_error_handler([$this, 'customErrorHandler']); + $written = syslog($level, $entry); + restore_error_handler(); - /** - * {@inheritdoc} - * @throws InternalServerErrorException if the syslog isn't available - */ - public function log($level, $message, array $context = array()) - { - $logLevel = $this->mapLevelToPriority($level); - $this->addEntry($logLevel, $message, $context); + if (!$written) { + throw new \UnexpectedValueException(sprintf('Can\'t write into syslog for ident "%s" and facility "%s": ' . $this->errorMessage, $this->channel, $this->logFacility)); + } } }