Logging is an important part of modern application development, regardless of the platform targeted or framework used. Logging provides a window into your app, enabling transparency and visibility. With proper logging, we can proactively identify points of failure and performance bottlenecks in our app, among other things.

The Laravel framework has always come with logging configured and enabled out of the box, but Laravel 5.6 introduced a new logging system. It’s more powerful, but also a bit more complicated. In this article, we’ll explore how it works.

First, let’s review how Laravel managed logging in earlier versions.

Logging before version 5.6

Prior to version 5.6, logging was mostly file-based. Laravel supported four log outputs:

  1. Single files: writing all the app’s logs to a single file. This file was called laravel.log by default, and it was located in the storage/logs/ folder.
  2. Daily files. With this, you would have a new file created for each day’s logs, resulting in the storage/logs/ folder containing files like laravel-2018-4-27.log, laravel-2018-4-28.log, and so forth.
  3. The system log, or syslog. This would send logs to the system logger. The location of these logs would vary depending on the operating system.
  4. The error log. In this mode, logs are written to your web server’s configured error log file. For instance, with Apache on a Linux OS, logs could be in /var/log/apache2/error.log.

Laravel allowed you to configure the log output by setting the log value in your config/app.php:

    'log' => 'single', // or 'daily', 'syslog', or 'errorlog'

You could also set a minimum log level. Errors and log events which fall below this level would not be logged.

    'log_level' => 'error',

Making logging first-class in Laravel 5.6

Laravel 5.6 introduces the concept of channels and stacks to allow logging to multiple locations simultaneously and provide greater customization and control to the developer.

Laravel 5.6 moves away from the file-based logging paradigm, by supporting a variety of possible log outputs, known as drivers. Supported drivers include the legacy single, daily, syslog, and errorlog as well as some new ones:

  • slack: sends messages in realtime to a configured Slack channel
  • monolog: allows you to use any other Monolog log handler. For instance, you can use the SendGridHandler to send logs via email
  • custom: allows you to send logs wherever you wish. You can write a logger that sends logs to a third-party service or product such as Logstash, Apache Kafka, or even to Pusher.

Channels are log configurations which make use of drivers. For instance, in the configuration below, we have a channel named regular that uses the single file driver, a channel named omg-the-house-is-on-fire that posts to slack, and another one named suspicious-stuff-going-on that uses a monolog driver:

    return [
      ...,
      'channels' => [
        'regular' => [
          'driver' => 'single',
        ],

        'omg-the-house-is-on-fire' => [
          'driver' => 'slack',
        ],

        'suspicious-stuff-going-on' => [
          'driver' => 'monolog',
        ],
      ],
    ];

Configuration

Log configuration now lives in config/logging.php. Each log channel comes with its own configuration. This allows you to define a minimum log level for that channel only. For instance, to specify that all logs sent to the single channel should be logged, but only those of critical level and above should be posted to Slack, you would do this:

    return [
      ...
      'channels' => [
        'single' => [
          'driver' => 'single',
          'level' => 'debug',
        ],

        'slack' => [
          'driver' => 'slack',
          'url' => env('LOG_SLACK_WEBHOOK_URL'),
          'level' => 'critical',
        ],
      ],
    ];

For channels which use the slack driver, you’d need to set a url, which is the webhook URL Slack provides you with. You can also set a username and emoji icon for the Slack app.

You can specify the channel to send logs to by specifying the default in config/logging.php:

    'default' => 'single',

Logging to multiple channels

Laravel 5.6 allows us to log to multiple channels simultaneously via a driver called stack. As the name implies, a stack combines multiple channels into one. This is extremely useful because it means you can send those critical logs to Slack or email and still have them in your regular log files for easy reference. Here’s how you can create a stack:

    // config/logging.php

    return [
       ...
      'channels' => [
        'stack' => [
          'driver' => 'stack',
          'name' => 'channel-name',
          'channels' => ['single', 'slack'],
        ],

        'single' => [
          'driver' => 'single',
          'level' => 'debug',
        ],

        'slack' => [
          'driver' => 'slack',
          'url' => env('LOG_SLACK_WEBHOOK_URL'),
          'level' => 'critical',
        ],
        ...
    ];

Once stack is set as your default channel, all logs would be sent to all channels registered for that stack, following their respective configurations, including the log levels. To achieve this in previous versions, you’d need to write a custom Monolog handler that combined multiple loggers.

Laravel 5.6 comes with stack set as the default log channel and the single sub-channel registered on the stack.

Writing log messages

While Laravel automatically logs all uncaught errors and exceptions thrown in your app, sometimes you may wish to log specific information or handle errors in a custom way. You can send logs manually using the Log facade ( Illuminate\Support\Facades\Log):

    Log::emergency($message);
    Log::alert($message);
    Log::critical($message);
    Log::error($message);
    Log::warning($message);
    Log::notice($message);
    Log::info($message);
    Log::debug($message);

The provided methods correspond to the various possible log levels, as defined by a specification.

Using these methods will send logs to your configured default channel. If you need to log to a specific channel rather than the default, you can call the channel method first:

    Log::channel('suspicious-activity')->warning($suspiciousActivity);

Even better, you can create a stack dynamically using the stack method:

    Log::stack(['suspicious-activity', 'slack'])->info("We're being attacked!");

Conclusion

Logging is an important part of software engineering. The twelve-factor manifesto treats proper logging as one of the key concerns of a modern app and gives a few tips on how logs should be treated. With these new logging features in Laravel, it’s easier than ever before to track down errors and improve your development experience as a whole.