Search code examples
phplaravellogginglaravel-5.3

Laravel 5.3 changing logfiles for specific console commands


There are two noisy console commands in my Laravel 5.3 app that I want to keep logs for but would prefer to have them write to a different log file from the rest of the system.

Currently my app writes logs to a file configured in bootstrap/app.php using $app->configureMonologUsing(function($monolog) { ...

Second prize is writing all console commands to another log file, but ideally just these two.

I tried following these instructions (https://blog.muya.co.ke/configure-custom-logging-in-laravel-5/ and https://laracasts.com/discuss/channels/general-discussion/advance-logging-with-laravel-and-monolog) to reroute all console logs to another file but it did not work and just caused weird issues in the rest of the code.

If this is still the preferred method in 5.3 then I will keep trying, but was wondering if there was newer method or a method to only change the file for those two console commands.


Solution

  • This is possible but first you need to remove existing handlers.

    Monolog already has had some logging handlers set, so you need to get rid of those with $monolog->popHandler();. Then using Wistar's suggestion a simple way of adding a new log is with $log->useFiles('/var/log/nginx/ds.console.log', $level='info');.

    public function fire (Writer $log)
    {
        $monolog = $log->getMonolog();
        $monolog->popHandler();
    
        $log->useFiles('/var/log/nginx/ds.console.log', $level='info');
        $log->useFiles('/var/log/nginx/ds.console.log', $level='error');
        ...
    

    For multiple handlers

    If you have more than one log handler set (if for example you are using Sentry) you may need to pop more than one before the handlers are clear. If you want to keep a handler, you need to loop through all of them and then readd the ones you wanted to keep.

    $monolog->popHandler() will throw an exception if you try to pop a non-existant handler so you have to jump through hoops to get it working.

    public function fire (Writer $log)
    {
        $monolog = $log->getMonolog();
    
        $handlers = $monolog->getHandlers();
        $numberOfHandlers = count($handlers);
        $saveHandlers = [];
    
        for ($idx=0; $idx<$numberOfHandlers; $idx++)
        {
            $handler = $monolog->popHandler();
    
            if (get_class($handler) !== 'Monolog\Handler\StreamHandler')
            {
                $saveHandlers[] = $handler;
            }
        }
    
        foreach ($saveHandlers as $handler)
        {
            $monolog->pushHandler($handler);
        }
    
        $log->useFiles('/var/log/nginx/ds.console.log', $level='info');
        $log->useFiles('/var/log/nginx/ds.console.log', $level='error');
        ...
    

    For more control over the log file, instead of $log->useFiles() you can use something like this:

    $logStreamHandler = new \Monolog\Handler\StreamHandler('/var/log/nginx/ds.console.log');
    
    $pid = getmypid();
    
    $logFormat = "%datetime% $pid [%level_name%]: %message%\n";
    $formatter = new \Monolog\Formatter\LineFormatter($logFormat, null, true);
    $logStreamHandler->setFormatter($formatter);
    
    $monolog->pushHandler($logStreamHandler);