2019-02-27 16:40:35 +01:00
< ? php
namespace Friendica\Util\Logger ;
use Friendica\Network\HTTPException\InternalServerErrorException ;
2019-02-28 08:56:28 +01:00
use Friendica\Util\Introspection ;
2019-02-27 16:40:35 +01:00
use Psr\Log\LogLevel ;
/**
* A Logger instance for syslogging ( fast , but simple )
* @ see http :// php . net / manual / en / function . syslog . php
*/
2019-03-04 08:42:08 +01:00
class SyslogLogger extends AbstractLogger
2019-02-27 16:40:35 +01:00
{
2019-02-27 16:46:53 +01:00
const IDENT = 'Friendica' ;
2019-02-27 16:40:35 +01:00
/**
* Translates LogLevel log levels to syslog log priorities .
2019-02-27 17:29:32 +01:00
* @ var array
2019-02-27 16:40:35 +01:00
*/
private $logLevels = [
2019-02-27 17:29:32 +01:00
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 ,
2019-02-27 16:40:35 +01:00
LogLevel :: EMERGENCY => LOG_EMERG ,
];
2019-02-27 17:29:32 +01:00
/**
* 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'
];
2019-02-27 16:40:35 +01:00
/**
* 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 ;
2019-03-04 23:49:37 +01:00
/**
* A error message of the current operation
* @ var string
*/
private $errorMessage ;
2019-02-27 16:40:35 +01:00
/**
2019-02-28 09:41:31 +01:00
* { @ inheritdoc }
2019-03-03 20:32:27 +01:00
* @ 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
2019-02-28 08:56:28 +01:00
*
* @ throws \Exception
2019-02-27 16:40:35 +01:00
*/
2019-03-03 20:32:27 +01:00
public function __construct ( $channel , Introspection $introspection , $level = LogLevel :: NOTICE , $logOpts = LOG_PID , $logFacility = LOG_USER )
2019-02-27 16:40:35 +01:00
{
2019-03-03 20:32:27 +01:00
parent :: __construct ( $channel , $introspection );
2019-02-27 16:40:35 +01:00
$this -> logOpts = $logOpts ;
$this -> logFacility = $logFacility ;
$this -> logLevel = $this -> mapLevelToPriority ( $level );
2019-02-28 09:41:31 +01:00
$this -> introspection -> addClasses ( array ( self :: class ));
2019-02-27 16:40:35 +01:00
}
2019-02-28 09:48:55 +01:00
/**
* 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 );
2019-03-03 20:32:27 +01:00
if ( $logLevel > $this -> logLevel ) {
2019-02-28 09:48:55 +01:00
return ;
}
$formattedLog = $this -> formatLog ( $logLevel , $message , $context );
$this -> write ( $logLevel , $formattedLog );
}
2019-02-27 16:40:35 +01:00
/**
* 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 )) {
2019-03-04 23:39:14 +01:00
throw new \InvalidArgumentException ( sprintf ( 'The level "%s" is not valid.' , $level ));
2019-02-27 16:40:35 +01:00
}
return $this -> logLevels [ $level ];
}
2019-02-28 09:48:55 +01:00
/**
* Closes the Syslog
*/
public function close ()
{
closelog ();
}
2019-02-27 16:40:35 +01:00
/**
* Writes a message to the syslog
2019-02-27 17:03:01 +01:00
* @ 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
2019-02-27 16:40:35 +01:00
*
* @ throws InternalServerErrorException if syslog cannot be used
*/
private function write ( $priority , $message )
{
2019-03-04 23:49:37 +01:00
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 ));
2019-02-27 16:40:35 +01:00
}
2019-03-04 23:39:14 +01:00
$this -> syslogWrapper ( $priority , $message );
2019-02-27 16:40:35 +01:00
}
2019-02-27 16:46:53 +01:00
/**
* Formats a log record for the syslog output
*
2019-02-27 17:03:01 +01:00
* @ param int $level The loglevel / priority
2019-02-27 16:46:53 +01:00
* @ param string $message The message
2019-02-27 17:03:01 +01:00
* @ param array $context The context of this call
2019-02-27 16:46:53 +01:00
*
* @ return string the formatted syslog output
*/
2019-02-27 16:40:35 +01:00
private function formatLog ( $level , $message , $context = [])
{
2019-02-27 17:29:32 +01:00
$record = $this -> introspection -> getRecord ();
$record = array_merge ( $record , [ 'uid' => $this -> logUid ]);
2019-02-27 17:03:01 +01:00
$logMessage = '' ;
2019-02-27 16:40:35 +01:00
2019-02-27 16:46:53 +01:00
$logMessage .= $this -> channel . ' ' ;
2019-02-27 17:29:32 +01:00
$logMessage .= '[' . $this -> logToString [ $level ] . ']: ' ;
2019-02-27 17:03:01 +01:00
$logMessage .= $this -> psrInterpolate ( $message , $context ) . ' ' ;
$logMessage .= @ json_encode ( $context ) . ' - ' ;
2019-02-27 17:29:32 +01:00
$logMessage .= @ json_encode ( $record );
2019-02-27 16:40:35 +01:00
return $logMessage ;
}
2019-03-04 23:39:14 +01:00
2019-03-04 23:49:37 +01:00
private function customErrorHandler ( $code , $msg )
{
$this -> errorMessage = preg_replace ( '{^(fopen|mkdir)\(.*?\): }' , '' , $msg );
}
2019-03-04 23:39:14 +01:00
/**
* A syslog wrapper to make syslog functionality testable
*
* @ param int $level The syslog priority
* @ param string $entry The message to send to the syslog function
*/
protected function syslogWrapper ( $level , $entry )
{
2019-03-04 23:49:37 +01:00
set_error_handler ([ $this , 'customErrorHandler' ]);
$written = syslog ( $level , $entry );
restore_error_handler ();
if ( ! $written ) {
throw new \UnexpectedValueException ( sprintf ( 'Can\'t write into syslog for ident "%s" and facility "%s": ' . $this -> errorMessage , $this -> channel , $this -> logFacility ));
}
2019-03-04 23:39:14 +01:00
}
2019-02-27 16:40:35 +01:00
}