]> git.mxchange.org Git - friendica.git/blob - src/Util/Logger/SyslogLogger.php
Code cleanup
[friendica.git] / src / Util / Logger / SyslogLogger.php
1 <?php
2
3 namespace Friendica\Util\Logger;
4
5 use Friendica\Network\HTTPException\InternalServerErrorException;
6 use Psr\Log\InvalidArgumentException;
7 use Psr\Log\LoggerInterface;
8 use Psr\Log\LogLevel;
9
10 /**
11  * A Logger instance for syslogging (fast, but simple)
12  * @see http://php.net/manual/en/function.syslog.php
13  */
14 class SyslogLogger implements LoggerInterface
15 {
16         const IDENT = 'Friendica';
17
18         /**
19          * Translates LogLevel log levels to syslog log priorities.
20          */
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,
30         ];
31
32         /**
33          * The channel of the current process (added to each message)
34          * @var string
35          */
36         private $channel;
37
38         /**
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
41          *
42          * @var int
43          */
44         private $logOpts;
45
46         /**
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
49          *
50          * @var int
51          */
52         private $logFacility;
53
54         /**
55          * The minimum loglevel at which this logger will be triggered
56          * @var int
57          */
58         private $logLevel;
59
60         /**
61          * The Introspector for the current call
62          * @var Introspection
63          */
64         private $introspection;
65
66         /**
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
71          */
72         public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
73         {
74                 $this->channel = $channel;
75                 $this->logOpts = $logOpts;
76                 $this->logFacility = $logFacility;
77                 $this->logLevel = $this->mapLevelToPriority($level);
78                 $this->introspection = $introspection;
79         }
80
81         /**
82          * Maps the LogLevel (@see LogLevel ) to a SysLog priority (@see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters )
83          *
84          * @param string $level A LogLevel
85          *
86          * @return int The SysLog priority
87          *
88          * @throws \Psr\Log\InvalidArgumentException If the loglevel isn't valid
89          */
90         public function mapLevelToPriority($level)
91         {
92                 if (!array_key_exists($level, $this->logLevels)) {
93                         throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.');
94                 }
95
96                 return $this->logLevels[$level];
97         }
98
99         /**
100          * Writes a message to the syslog
101          * @see http://php.net/manual/en/function.syslog.php#refsect1-function.syslog-parameters
102          *
103          * @param int    $priority The Priority
104          * @param string $message  The message of the log
105          *
106          * @throws InternalServerErrorException if syslog cannot be used
107          */
108         private function write($priority, $message)
109         {
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 . '""');
112                 }
113
114                 syslog($priority, $message);
115         }
116
117         /**
118          * Closes the Syslog
119          */
120         public function close()
121         {
122                 closelog();
123         }
124
125         /**
126          * Formats a log record for the syslog output
127          *
128          * @param int    $level   The loglevel/priority
129          * @param string $message The message
130          * @param array  $context The context of this call
131          *
132          * @return string the formatted syslog output
133          */
134         private function formatLog($level, $message, $context = [])
135         {
136                 $logMessage = '';
137
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());
143
144                 return $logMessage;
145         }
146
147         /**
148          * Simple interpolation of PSR-3 compliant replacements ( between '{' and '}' )
149          * @see https://www.php-fig.org/psr/psr-3/#12-message
150          *
151          * @param string $message
152          * @param array  $context
153          *
154          * @return string the interpolated message
155          */
156         private function psrInterpolate($message, array $context = array())
157         {
158                 $replace = [];
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);
165                         }
166                 }
167
168                 return strtr($message, $replace);
169         }
170
171         /**
172          * Adds a new entry to the syslog
173          *
174          * @param int    $level
175          * @param string $message
176          * @param array  $context
177          *
178          * @throws InternalServerErrorException if the syslog isn't available
179          */
180         private function addEntry($level, $message, $context = [])
181         {
182                 if ($level >= $this->logLevel) {
183                         return;
184                 }
185
186                 $formattedLog = $this->formatLog($level, $message, $context);
187                 $this->write($level, $formattedLog);
188         }
189
190         /**
191          * {@inheritdoc}
192          * @throws InternalServerErrorException if the syslog isn't available
193          */
194         public function emergency($message, array $context = array())
195         {
196                 $this->addEntry(LOG_EMERG, $message, $context);
197         }
198
199         /**
200          * {@inheritdoc}
201          * @throws InternalServerErrorException if the syslog isn't available
202          */
203         public function alert($message, array $context = array())
204         {
205                 $this->addEntry(LOG_ALERT, $message, $context);
206         }
207
208         /**
209          * {@inheritdoc}
210          * @throws InternalServerErrorException if the syslog isn't available
211          */
212         public function critical($message, array $context = array())
213         {
214                 $this->addEntry(LOG_CRIT, $message, $context);
215         }
216
217         /**
218          * {@inheritdoc}
219          * @throws InternalServerErrorException if the syslog isn't available
220          */
221         public function error($message, array $context = array())
222         {
223                 $this->addEntry(LOG_ERR, $message, $context);
224         }
225
226         /**
227          * {@inheritdoc}
228          * @throws InternalServerErrorException if the syslog isn't available
229          */
230         public function warning($message, array $context = array())
231         {
232                 $this->addEntry(LOG_WARNING, $message, $context);
233         }
234
235         /**
236          * {@inheritdoc}
237          * @throws InternalServerErrorException if the syslog isn't available
238          */
239         public function notice($message, array $context = array())
240         {
241                 $this->addEntry(LOG_NOTICE, $message, $context);
242         }
243
244         /**
245          * {@inheritdoc}
246          * @throws InternalServerErrorException if the syslog isn't available
247          */
248         public function info($message, array $context = array())
249         {
250                 $this->addEntry(LOG_INFO, $message, $context);
251         }
252
253         /**
254          * {@inheritdoc}
255          * @throws InternalServerErrorException if the syslog isn't available
256          */
257         public function debug($message, array $context = array())
258         {
259                 $this->addEntry(LOG_DEBUG, $message, $context);
260         }
261
262         /**
263          * {@inheritdoc}
264          * @throws InternalServerErrorException if the syslog isn't available
265          */
266         public function log($level, $message, array $context = array())
267         {
268                 $logLevel = $this->mapLevelToPriority($level);
269                 $this->addEntry($logLevel, $message, $context);
270         }
271 }