namespace Friendica\Util\Logger;
use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Introspection;
+use Friendica\Util\Strings;
use Psr\Log\InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
*/
class SyslogLogger implements LoggerInterface
{
+ const IDENT = 'Friendica';
+
/**
* Translates LogLevel log levels to syslog log priorities.
+ * @var array
*/
private $logLevels = [
LogLevel::DEBUG => LOG_DEBUG,
];
/**
- * The standard ident of the syslog (added to each message)
+ * Translates log priorities to string outputs
+ * @var array
+ */
+ 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'
+ ];
+
+ /**
+ * The channel of the current process (added to each message)
* @var string
*/
- private $ident;
+ private $channel;
/**
* Indicates what logging options will be used when generating a log message
private $logLevel;
/**
- * The Introspector for the current call
+ * The Introspection 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
+ * The UID of the current call
+ * @var string
+ */
+ private $logUid;
+
+ /**
+ * @param string $channel The output channel
+ * @param Introspection $introspection The introspection of the current call
+ * @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
+ * @throws \Exception
*/
public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
{
- $this->ident = $channel;
+ $this->logUid = Strings::getRandomHex(6);
+ $this->channel = $channel;
$this->logOpts = $logOpts;
$this->logFacility = $logFacility;
$this->logLevel = $this->mapLevelToPriority($level);
/**
* Writes a message to the syslog
+ * @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters
+ *
+ * @param int $priority The Priority
+ * @param string $message The message of the log
*
- * @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 . '""');
+ if (!openlog(self::IDENT, $this->logOpts, $this->logFacility)) {
+ throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->channel . '" and facility "' . $this->logFacility . '""');
}
syslog($priority, $message);
}
+ /**
+ * Closes the Syslog
+ */
public function close()
{
closelog();
}
+ /**
+ * Formats a log record for the syslog output
+ *
+ * @param int $level The loglevel/priority
+ * @param string $message The message
+ * @param array $context The context of this call
+ *
+ * @return string the formatted syslog output
+ */
private function formatLog($level, $message, $context = [])
{
- $logMessage = '';
+ $record = $this->introspection->getRecord();
+ $record = array_merge($record, ['uid' => $this->logUid]);
+ $logMessage = '';
- $logMessage .= $this->ident . ' ';
- $logMessage .= '[' . $level . ']: ';
- $logMessage .= $message . ' ';
- $logMessage .= json_encode($context) . ' - ';
- $logMessage .= json_encode($this->introspection->getRecord());
+ $logMessage .= $this->channel . ' ';
+ $logMessage .= '[' . $this->logToString[$level] . ']: ';
+ $logMessage .= $this->psrInterpolate($message, $context) . ' ';
+ $logMessage .= @json_encode($context) . ' - ';
+ $logMessage .= @json_encode($record);
return $logMessage;
}
+ /**
+ * Simple interpolation of PSR-3 compliant replacements ( variables 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())
+ {
+ $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);
+ }
+
+ /**
+ * Adds a new entry to the syslog
+ *
+ * @param int $level
+ * @param string $message
+ * @param array $context
+ *
+ * @throws InternalServerErrorException if the syslog isn't available
+ */
private function addEntry($level, $message, $context = [])
{
if ($level >= $this->logLevel) {
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function emergency($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function alert($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function critical($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function error($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function warning($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function notice($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function info($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function debug($message, array $context = array())
{
/**
* {@inheritdoc}
+ * @throws InternalServerErrorException if the syslog isn't available
*/
public function log($level, $message, array $context = array())
{