Logger to syslog with error_log fallback Below is an implementation of a typical Logger interface that uses syslog() as the default means to store messages and falls back to error_log() if that does not work.  This is the first time I've used syslog and just want to ensure that my code is logical and there isn't something about syslog that I'm not aware of that could come back and bite me.  The code does work, messages are appearing in syslog as appropriate.


Project github repo
Source code for Logger interface
Source code for extended class




&lt;?php

/**
 * @file
 * @brief Holds a class that implements error logging to an OS or system logger.
 */

namespace SprayFire\Logger;

/**
 * @brief A SprayFire.Logger.Log implementation that will attempt to log a message
 * to syslog and as a fallback will log the message to whatever option is set in
 * the error_log php.ini directive.
 *
 * @uses SprayFire.Logger.Log
 * @uses SprayFire.Core.CoreObject
 */
class SystemLogger extends \SprayFire\Core\CoreObject implements \SprayFire\Logger\Log {

    /**
     * @brief A flag to tell if fallback should be used.
     *
     * @property $syslogOpened
     */
    protected $syslogOpened = false;

    /**
     * @param $syslogIdent
     */
    public function __construct($syslogIdent = 'SprayFire') {
        $this-&gt;syslogOpened = \openlog($syslogIdent, \LOG_NDELAY, \LOG_USER);
    }

    /**
     * @param $syslogSeverity The timestamp for the \a $message being logged
     * @param $message The message to be logged
     * @return true if the message was logged, false if not
     */
    public function log($syslogSeverity, $message) {
        $loggedtoSyslog = $this-&gt;logToSyslog($syslogSeverity, $message);
        if (!$loggedtoSyslog) {
            return $this-&gt;logToErrorLog($message);
        }
        return $loggedToSyslog;
    }

    /**
     * @param $syslogSeverity The severity of the syslog message to store
     * @param $message The message to store in syslog
     * @return true if the message was logged into syslog, false if it wasn't
     * @see http://www.php.net/syslog
     */
    protected function logToSyslog($syslogSeverity, $message) {
        if ($this-&gt;syslogOpened) {
            return \syslog($syslogSeverity, $message);
        }
        return false;
    }

    /**
     * @param $message The message to log with error_log
     * @return true if logged, false if not
     * @see http://php.net/manual/en/function.error-log.php
     */
    protected function logToErrorLog($message) {
        $message = \date('M-d-Y H:i:s') . ' := ' . $message;
        return \error_log($message);
    }

    /**
     * @brief Ensures that the syslog is properly closed if it was opened.
     */
    public function __destruct() {
        if ($this-&gt;syslogOpened) {
            \closelog();
        }
    }

}


One question that I would have is how to unit test this?  How would I disable syslog to ensure the fallback is called?  Or is there some other way to do it?
You need a SysLog interface and a SysLogImpl implementation class.

public interface SysLog {
    public bool openlog(string $ident, int $option ,int $facility);
    public bool syslog (int $priority, string $message);
    public bool closelog (void);
}


SysLogImpl just delegate the calls to the openlog, syslog and closelog PHP functions.

public class SysLogImpl implements SysLog {
    public bool openlog(string $ident, int $option, int $facility) {
        return openlog($ident, $option, $facility);
    }

    public bool syslog(int $priority, string $message) {
        return syslog($priority, $message);
    }

    public bool closelog (void) {
        return closelog();
    }
} 


It's important that this class should be just a thin facade without any logic since this class usually doesn't have any test or just has a few integration tests.

In production code pass a SysLogImpl instance to the constructor of your SystemLogger class and use this SysLogImpl instance instead of direct PHP calls.

In test code pass a mocked or a custom object which implements the SysLog interface to the SystemLogger class. The passed object should be able to store function calls and emulate responses (as mock objects do).

Maybe you'll need an ErrorLog interface and an ErrorLogImpl implementation class which logs with error_log PHP calls.



Another solution is a Test-Specific Subclass. You can extract out the openlog call from the constructor to a separate function and create a subclass of the SystemLogger which overrides this method and returns false.



A better and generic design would be creating a common Logger interface and a SyslogLogger and an ErrorLogLogger implementation classes. Both implements Logger. Then the SystemLogger class get one or more Logger instances and try calling their open functions and use that implementation whose open function returns true first.

If you use this the SystemLogger has only one responsibility: managing loggers, while SyslogLogger cares only about syslog and ErrorLogLogger cares only about error_log logging.