How to Customize PHP Monolog

Big Lebowski Only a fool writes their own PHP logger. Smart PHP programmers use Monolog. Here’s how you can customize Monolog using inheritance.

My problem was that I wanted to programmatically control what gets in the logs. So if I’m in development, I want to see all of the logging down to DEBUG level. But when I’m in production, I only want to see anything at a log level of INFO or greater.

For reference, Monolog supports the following log levels.

DEBUG (100): Detailed debug information.
INFO (200): Interesting events. Examples: User logs in, SQL logs.
NOTICE (250): Normal but significant events.
WARNING (300): Exceptional occurrences that are not errors. Examples: Use of deprecated APIs, poor use of an API, undesirable things that are not necessarily wrong.
ERROR (400): Runtime errors that do not require immediate action but should typically be logged and monitored.
CRITICAL (500): Critical conditions. Example: Application component unavailable, unexpected exception.
ALERT (550): Action must be taken immediately. Example: Entire website down, database unavailable, etc. This should trigger the SMS alerts and wake you up.
EMERGENCY (600): Emergency: system is unusable.

To do that I extended Monolog’s StreamHandler like this. I chose StreamHandler because it can handle writing to a log file. There are many handlers that you can leverage to meet your needs.

class DurpLoggerHandler extends StreamHandler {
    public function __construct() {
        parent::__construct(Configuration::logFile(), Configuration::logLevel());
        $formatter = new LineFormatter(NULL, NULL, FALSE, TRUE);
        $this->setFormatter($formatter);
    }

    protected function write(array $record) {
        if (!($this->getLevel() >= Configuration::logLevel())) {
            return;
        }
        parent::write($record);
    }
}

The important part is overriding the write() method. If the log level is greater than or equal to the level I’m setting in my Configuration class.

The Configuration class could look like this, although mine is slightly more complicated because I’m pulling values from a configuration file rather than setting them in the Configuration source with Logger::DEBUG (I don’t like to have to modify any source code on production).

use Monolog\Logger;

class Configuration {
    const LOG_LEVEL = Logger::DEBUG; 
    const LOG_FILE = '../logs/application.log';

    public static function loglevel() {
        return self::LOG_LEVEL;
    }

    public static function logFile() {
        return self::LOG_FILE;
    }
}

And the to use this custom Monolog logger, I do this in my application.

$this->logger = new DurpLogger('durp-service');
$this->logger->pushHandler(new DurpLoggerHandler());
$this->logger->addDebug('Durp Service Started');
$this->logger->addInfo("That's like your opinion, man.");

That’s it. Now if I change my Configuration::LOG_LEVEL to something greater than DEBUG, like say INFO, I won’t see “Durp Service Started,” but I’ll still get “That’s like your opinion, man.” The dude abides.

If you’re curious about that LineFormatter stuff, That’s just me tweaking the LineFormatting to not include a couple of trailing brackets [] [] that are automatically included with each Monolog log entry. They have a purpose, but I don’t need them.

(h/t) Seldaek answer on Stack Overflow about the Line Formatter (h/t) chalasr Stack Overflow member helped me think through some of this logic here

Share Button

One Comment

Leave a Reply

Your email address will not be published.