3 namespace Friendica\Util\Logger;
5 use Friendica\Network\HTTPException\InternalServerErrorException;
6 use Psr\Log\InvalidArgumentException;
7 use Psr\Log\LoggerInterface;
11 * A Logger instance for syslogging (fast, but simple)
12 * @see http://php.net/manual/en/function.syslog.php
14 class SyslogLogger implements LoggerInterface
16 const IDENT = 'Friendica';
19 * Translates LogLevel log levels to syslog log priorities.
21 private $logLevels = [
22 LogLevel::DEBUG => LOG_DEBUG,
23 LogLevel::INFO => LOG_INFO,
24 LogLevel::NOTICE => LOG_NOTICE,
25 LogLevel::WARNING => LOG_WARNING,
26 LogLevel::ERROR => LOG_ERR,
27 LogLevel::CRITICAL => LOG_CRIT,
28 LogLevel::ALERT => LOG_ALERT,
29 LogLevel::EMERGENCY => LOG_EMERG,
33 * The channel of the current process (added to each message)
39 * Indicates what logging options will be used when generating a log message
40 * @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters
47 * Used to specify what type of program is logging the message
48 * @see http://php.net/manual/en/function.openlog.php#refsect1-function.openlog-parameters
55 * The minimum loglevel at which this logger will be triggered
61 * The Introspector for the current call
64 private $introspection;
67 * @param string $channel The output channel
68 * @param string $level The minimum loglevel at which this logger will be triggered
69 * @param int $logOpts Indicates what logging options will be used when generating a log message
70 * @param int $logFacility Used to specify what type of program is logging the message
72 public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
74 $this->channel = $channel;
75 $this->logOpts = $logOpts;
76 $this->logFacility = $logFacility;
77 $this->logLevel = $this->mapLevelToPriority($level);
78 $this->introspection = $introspection;
82 * Maps the LogLevel (@see LogLevel ) to a SysLog priority (@see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters )
84 * @param string $level A LogLevel
86 * @return int The SysLog priority
88 * @throws \Psr\Log\InvalidArgumentException If the loglevel isn't valid
90 public function mapLevelToPriority($level)
92 if (!array_key_exists($level, $this->logLevels)) {
93 throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.');
96 return $this->logLevels[$level];
100 * Writes a message to the syslog
101 * @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters
103 * @param int $priority The Priority
104 * @param string $message The message of the log
106 * @throws InternalServerErrorException if syslog cannot be used
108 private function write($priority, $message)
110 if (!openlog(self::IDENT, $this->logOpts, $this->logFacility)) {
111 throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->channel . '" and facility "' . $this->logFacility . '""');
114 syslog($priority, $message);
120 public function close()
126 * Formats a log record for the syslog output
128 * @param int $level The loglevel/priority
129 * @param string $message The message
130 * @param array $context The context of this call
132 * @return string the formatted syslog output
134 private function formatLog($level, $message, $context = [])
138 $logMessage .= $this->channel . ' ';
139 $logMessage .= '[' . $level . ']: ';
140 $logMessage .= $this->psrInterpolate($message, $context) . ' ';
141 $logMessage .= @json_encode($context) . ' - ';
142 $logMessage .= @json_encode($this->introspection->getRecord());
148 * Simple interpolation of PSR-3 compliant replacements ( between '{' and '}' )
149 * @see https://www.php-fig.org/psr/psr-3/#12-message
151 * @param string $message
152 * @param array $context
154 * @return string the interpolated message
156 private function psrInterpolate($message, array $context = array())
159 foreach ($context as $key => $value) {
160 // check that the value can be casted to string
161 if (!is_array($value) && (!is_object($value) || method_exists($value, '__toString'))) {
162 $replace['{' . $key . '}'] = $value;
163 } elseif (is_array($value)) {
164 $replace['{' . $key . '}'] = @json_encode($value);
168 return strtr($message, $replace);
172 * Adds a new entry to the syslog
175 * @param string $message
176 * @param array $context
178 * @throws InternalServerErrorException if the syslog isn't available
180 private function addEntry($level, $message, $context = [])
182 if ($level >= $this->logLevel) {
186 $formattedLog = $this->formatLog($level, $message, $context);
187 $this->write($level, $formattedLog);
192 * @throws InternalServerErrorException if the syslog isn't available
194 public function emergency($message, array $context = array())
196 $this->addEntry(LOG_EMERG, $message, $context);
201 * @throws InternalServerErrorException if the syslog isn't available
203 public function alert($message, array $context = array())
205 $this->addEntry(LOG_ALERT, $message, $context);
210 * @throws InternalServerErrorException if the syslog isn't available
212 public function critical($message, array $context = array())
214 $this->addEntry(LOG_CRIT, $message, $context);
219 * @throws InternalServerErrorException if the syslog isn't available
221 public function error($message, array $context = array())
223 $this->addEntry(LOG_ERR, $message, $context);
228 * @throws InternalServerErrorException if the syslog isn't available
230 public function warning($message, array $context = array())
232 $this->addEntry(LOG_WARNING, $message, $context);
237 * @throws InternalServerErrorException if the syslog isn't available
239 public function notice($message, array $context = array())
241 $this->addEntry(LOG_NOTICE, $message, $context);
246 * @throws InternalServerErrorException if the syslog isn't available
248 public function info($message, array $context = array())
250 $this->addEntry(LOG_INFO, $message, $context);
255 * @throws InternalServerErrorException if the syslog isn't available
257 public function debug($message, array $context = array())
259 $this->addEntry(LOG_DEBUG, $message, $context);
264 * @throws InternalServerErrorException if the syslog isn't available
266 public function log($level, $message, array $context = array())
268 $logLevel = $this->mapLevelToPriority($level);
269 $this->addEntry($logLevel, $message, $context);