Advance Logging with Laravel and Monolog

Published 2 years ago by ludo237

Hello everyone.

At the moment my only configuration on Logging events is Log::useDailyFiles(storage_path().'/logs/laravel_main.log');

But I think that I can do more than that. My main goal is to separate logging files for example for Log::info i'll use a file called laravel_info.log for Log::error I'll use a file called laravel_error.log etc.

How can I achieve this with the built-in logging functionality?

Best Answer (As Selected By ludo237)
toltech

True. Alright heres a direct answer to the question being asked in this thread (for L5):

You can override the Illuminate\Foundation\Bootstrap\ConfigureLogging Class in the Bootstrap directory (as the documentation states).

Steps are:

  1. Create a ConfigureLogging Class in the /bootstrap directory that implements the configureHandlers() method from Illuminate\Foundation\Bootstrap\ConfigureLogging.
  2. Open App/Console/Kernal.php. It extends ConsoleKernal, which has a $bootstrappers attribute. Overwrite that attribute in App/Console/Kernal.php and register your new /bootstrap/ConfigureLogging class:
// App/Console/Kernal.php
...
class Kernel extends ConsoleKernel {

    /**
     * OVERRIDING PARENT CLASS
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\SetRequestForConsole',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
        'Bootstrap\ConfigureLogging' // custom logger bootstrapper
    ];
...
  1. Do the same for app/Http/Kernal.php
  2. Register your new new bootstrap namespace in composer.json and run composer dumpautoload. My namespace is Bootstrap.
// composer.json
...
"autoload": {
        "psr-4": {
                "Bootstrap\\": "bootstrap/"
        }
    },
...
  1. Implement the custom handlers you want in the configureHandlers method in /bootstrap/ConfigureLogging. Something like:
// bootstrap/ConfigureLogging
<?php namespace Bootstrap;

use Monolog\Logger as Monolog;
use Illuminate\Log\Writer;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Bootstrap\ConfigureLogging as BaseConfigureLogging;
use Monolog\Handler\StreamHandler;

class ConfigureLogging extends BaseConfigureLogging{

    /**
     * OVERRIDE PARENT
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureHandlers(Application $app, Writer $log)
    {

        $bubble = false;

        // Stream Handlers
        $infoStreamHandler = new StreamHandler( storage_path("/logs/laravel_info.log"), Monolog::INFO, $bubble);
        $warningStreamHandler = new StreamHandler( storage_path("/logs/laravel_warning.log"), Monolog::WARNING, $bubble);
        $errorStreamHandler = new StreamHandler( storage_path("/logs/laravel_error.log"), Monolog::ERROR, $bubble);

        // Get monolog instance and push handlers
        $monolog = $log->getMonolog();
        $monolog->pushHandler($infoStreamHandler);
        $monolog->pushHandler($warningStreamHandler);
        $monolog->pushHandler($errorStreamHandler);

        $log->useDailyFiles($app->storagePath().'/logs/daily.log');
    }

One note on that snippet, the order you push the streamhandlers on is important: push the most restrictive on last, so that it is hit first. Otherwise, say if you pushed the infoStreamHandler on last, it would log everything above it also and an error wouldn't make it to the error handler.

This will result in 3 log files:

  • laravel_error.log - just errors
  • laravel_info.log - just info
  • laravel_warning.log - just warnings *daily log file - all

A related question was asked which I also answered.

moak
moak
2 years ago (1,060 XP)

I'd also like to see a laracast on best practices and ideas for logging

ludo237

Yea indeed @moak, let's log this to @jeffreyway :D

moak
moak
2 years ago (1,060 XP)

I made a suggestion here https://laracasts.com/discuss/channels/requests/logging-with-laravel If people support it will that maybe catch his attention?

bashy
bashy
2 years ago (1,083,200 XP)

I wanted to use something like this yesterday in one of my apps. Would like to see some options for multiple files as well. Having everything in one file makes it hard to find the relevant info.

ludo237

Indeed @bashy

bashy
bashy
2 years ago (1,083,200 XP)

Saw this yesterday: https://github.com/GrahamCampbell/Laravel-LogViewer

Haven't tried it but looks like it splits up the different types by reading the single file.

toltech

http://www.chrisduell.com/blog/development/using-custom-monolog-handlers-in-laravel/

This tutorial shows generally how to grab the Monolog instance from laravel and use a custom handler.

egeriis

@toltech That's for Laravel 4 :)

toltech

True. Alright heres a direct answer to the question being asked in this thread (for L5):

You can override the Illuminate\Foundation\Bootstrap\ConfigureLogging Class in the Bootstrap directory (as the documentation states).

Steps are:

  1. Create a ConfigureLogging Class in the /bootstrap directory that implements the configureHandlers() method from Illuminate\Foundation\Bootstrap\ConfigureLogging.
  2. Open App/Console/Kernal.php. It extends ConsoleKernal, which has a $bootstrappers attribute. Overwrite that attribute in App/Console/Kernal.php and register your new /bootstrap/ConfigureLogging class:
// App/Console/Kernal.php
...
class Kernel extends ConsoleKernel {

    /**
     * OVERRIDING PARENT CLASS
     * The bootstrap classes for the application.
     *
     * @var array
     */
    protected $bootstrappers = [
        'Illuminate\Foundation\Bootstrap\DetectEnvironment',
        'Illuminate\Foundation\Bootstrap\LoadConfiguration',
        'Illuminate\Foundation\Bootstrap\HandleExceptions',
        'Illuminate\Foundation\Bootstrap\RegisterFacades',
        'Illuminate\Foundation\Bootstrap\SetRequestForConsole',
        'Illuminate\Foundation\Bootstrap\RegisterProviders',
        'Illuminate\Foundation\Bootstrap\BootProviders',
        'Bootstrap\ConfigureLogging' // custom logger bootstrapper
    ];
...
  1. Do the same for app/Http/Kernal.php
  2. Register your new new bootstrap namespace in composer.json and run composer dumpautoload. My namespace is Bootstrap.
// composer.json
...
"autoload": {
        "psr-4": {
                "Bootstrap\\": "bootstrap/"
        }
    },
...
  1. Implement the custom handlers you want in the configureHandlers method in /bootstrap/ConfigureLogging. Something like:
// bootstrap/ConfigureLogging
<?php namespace Bootstrap;

use Monolog\Logger as Monolog;
use Illuminate\Log\Writer;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Bootstrap\ConfigureLogging as BaseConfigureLogging;
use Monolog\Handler\StreamHandler;

class ConfigureLogging extends BaseConfigureLogging{

    /**
     * OVERRIDE PARENT
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureHandlers(Application $app, Writer $log)
    {

        $bubble = false;

        // Stream Handlers
        $infoStreamHandler = new StreamHandler( storage_path("/logs/laravel_info.log"), Monolog::INFO, $bubble);
        $warningStreamHandler = new StreamHandler( storage_path("/logs/laravel_warning.log"), Monolog::WARNING, $bubble);
        $errorStreamHandler = new StreamHandler( storage_path("/logs/laravel_error.log"), Monolog::ERROR, $bubble);

        // Get monolog instance and push handlers
        $monolog = $log->getMonolog();
        $monolog->pushHandler($infoStreamHandler);
        $monolog->pushHandler($warningStreamHandler);
        $monolog->pushHandler($errorStreamHandler);

        $log->useDailyFiles($app->storagePath().'/logs/daily.log');
    }

One note on that snippet, the order you push the streamhandlers on is important: push the most restrictive on last, so that it is hit first. Otherwise, say if you pushed the infoStreamHandler on last, it would log everything above it also and an error wouldn't make it to the error handler.

This will result in 3 log files:

  • laravel_error.log - just errors
  • laravel_info.log - just info
  • laravel_warning.log - just warnings *daily log file - all

A related question was asked which I also answered.

jgreen
jgreen
2 years ago (15,675 XP)

This was helpful, however I was implementing a mysql connection for my logs using wazarri/monolog-mysql and ran into issues with the DB facade not being fully setup. Essentially, the Bootstrap/ConfigureLogging class was being bootstrapped before the DB was being setup. Here's how I fixed that in case anyone else is trying this. This is in app\Http\Kernel.php. I like this method because it not only replaces the Bootstrap for logging, but also moves it to the end of the stack so everything else is ready for use.

/**
     * Constructor
     */
    public function __construct(Application $app, Router $router)
    {
        parent::__construct($app, $router);

        foreach($this->bootstrappers as $key => $boostrapper)
        {
            if($boostrapper === 'Illuminate\Foundation\Bootstrap\ConfigureLogging')
            {
                unset($this->bootstrappers[$key]);

            }
        }
        $this->bootstrappers[] = 'Bootstrap\ConfigureLogging';
    }
jclermont

You can have full control over your log configuration without having to remove the built in ConfigureLogging bootstrapper. The app has a configureMonologUsing function which gives you everything you need. More details under "Custom Monolog Configuration" here: http://laravel.com/docs/5.1/errors#configuration

Phil_S

As @jclermont stated, you can easily do this without all the work mentioned in the best answer.

Inside bootstrap\app.php you can simply add the following:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$app->configureMonologUsing(function($monolog) {
    $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel_info.log'), Logger::INFO));
    $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel_warning.log'), Logger::WARNING));
});

Simply repeat the call to pushHandler() with your remaining severity levels.

ui-matt

@Phil_S, that works great.

Does anyone know how to setup monolog to send the same messages to two different places? For example, what if I wanted to send errors (and higher) to loggly while still having complete local log files? The bubble parameter indicates that this should be possible, but I can't seem to get it working.

femanso

@ui-matt, I'm not sure this is the best way, the following code writes Debug messages to laravel_debug.log AND laravel.log.

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$app->configureMonologUsing(function ($monolog) {
    $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel_debug.log'), Logger::DEBUG, false));
    $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel.log'), Logger::DEBUG, true));
    $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel.log'), Logger::INFO, false));
});
udgeet

Here log path is changed according to log levels. But what if, i have two different path for log files each contains log with all levels or custom configured level?

For example I have two paths for Api Logs and Sms Logs having different path.

How can i define two logs? currently logging is done like
ex Log::emergency('Log string'); This string is logged according to defined stream handler of emergency level.

But instead i want this log to write in API path log or Sms path log.

Sign In or create a forum account to participate in this discussion.