By Matej Elias
Last Validated on May 12 2021 · Originally Published on May 12, 2021 · Viewed 1k times

Introduction

In this tutorial, you will learn how to log the behaviour of your Laravel application. This will help you troubleshoot your application much more easily and quickly. Finding bugs can be frustrating and time-consuming. Luckily, Laravel comes with out of the box logging tools that will help a lot.

In application development, logging is crucial regardless of the platform or language. In this tutorial, we will take a look at one of the most popular PHP frameworks - Laravel.


Prerequisites

  • Laravel installed.
  • Basic knowledge of Laravel.

Step 1 — Getting To Know How Logging In Laravel Works

Laravel allows you to log messages about the behaviour of your application to files, the system of error logs, or even send them to Slack to notify your development team.

Laravel logging is based on channels. Channels are specific ways of writing a log message. Each channel may represent a different destination and you may send messages to multiple channels at once.

Monolog library

Laravel utilizes many popular open-source libraries. One of them is Monolog. It's a highly popular logging library among PHP developers, as it's easy to learn and very pleasant to work with. Monolog provides many ways to write logs such as writing them to files or sending them to email.

Feel free to learn more about Monolog in our Monolog basics logging tutorial.


Step 2 — Configuration

All the configuration options of your application's logging behaviour are located in the config/logging.php file. This file allows you to configure logging channels by adding new ones or updating current ones.

The configuration file contains one multidimensional array with two main keys. The first key is default and it looks like this:

'default' => env('LOG_CHANNEL', 'stack')

This key sets the default logging channel. Any log that will be sent without specifying the channel will be directed to the default channel. Out of the box, stack is set to be the default channel.

The second key is channels. This key contains the array of channels that can be used for logging. The default stack channel mentioned above is defined here. Out of the box, the array of channels looks like this (note that this is just a part of the array, for full array, see the config file):

'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single'],
            'ignore_exceptions' => false,
        ],

        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
        ],

        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
            'days' => 14,
        ],

        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => env('LOG_LEVEL', 'critical'),
        ],
				
				...
	
    ],

Channel Drivers

Every channel is powered by a driver. Driver sets where and how logs are recorded. You can think of drivers as Monolog handlers. Drivers are responsible for the actual logging to happen.

There are many built-in ready-to-use drivers or you can make your own. If you are familiar with the Monolog logging library, there is a monolog driver already created that can use any supported Monolog handler.

Most of those drivers are present in the configuration file out of the box, so it's recommended to take a look at them. This is the list of all channel drivers available in every Laravel application:

  • custom: a driver that calls a specified custom factory driver to create a channel
  • daily: a RotatingFileHandler based Monolog driver which rotates daily
  • errorlog: a ErrorLogHandler based Monolog driver
  • monolog: a Monolog factory driver that may use any supported Monolog handler
  • null: a driver that discards all log messages
  • papertrail: a SyslogUdpHandler based Monolog driver
  • single: a single file or path based logger channel (StreamHandler)
  • slack: a SlackWebhookHandler based Monolog driver
  • stack: a wrapper to facilitate creating "multi-channel" channels
  • syslog: a SyslogHandler based Monolog driver

Log Levels

Every channel has a level option. This option determines which log will or will not be processed based on the severity of the message. Less severe messages will not pass through the channel when the level is set high.

This is the full list of log levels

  • DEBUG: detailed debug information (LOWEST)
  • INFO : interesting events. Examples: User logs in, SQL logs
  • NOTICE: normal but significant events
  • WARNING: exceptional occurrences that are not errors. Examples: Use of deprecated APIs, poor use of an API, undesirable things that are not necessarily wrong
  • ERROR: runtime errors that do not require immediate action but should typically be logged and monitored
  • CRITICAL: critical conditions. Example: Application component unavailable, unexpected exception
  • ALERT: action must be taken immediately
  • EMERGENCY: emergency: system is unusable (HIGHEST)

Step 3 — Writing Log Messages

You can send messages to the log using the Log facade. Let's take a look at the following example:

use Illuminate\\Support\\Facades\\Log;

$message = "This is my first log";

Log::debug($message);
Log::warning($message);

Given the current un-edited configuration that comes out of the box with Laravel, this code will create two entries in the storage/logs/laravel.log that will look like this:

Output:
[2021-04-30 09:48:54] local.DEBUG: This is my first log  
[2021-04-30 09:48:54] local.WARNING: This is my first log

Writing To Specific Channels

By default, all logs are directed to the stack channel. If you want to send a message to a specific channel that is not the default channel, you can do that by calling channel() method on the Log facade.

use Illuminate\\Support\\Facades\\Log;

$message = "This is my first log";

Log::channel('daily')->debug($message);

Given the current configuration, this will create a new daily log storage/logs/laravel-YYY-MM-DD.log (based on the current date) that will be active for 14 days. In this log file you will see the following entry:

Output:
[2021-04-30 10:07:40] local.DEBUG: This is my first log

Contextual Information

You can also provide contextual data in the form of an array. This data will be formatted and printed alongside the log message.

For example, you can provide information such as the user that triggered the error or the current URL:

use Illuminate\\Support\\Facades\\Log;

Log::error("Error happend :(", ["id" => "12345", "username" => "some_guy"]);
Output:
[2021-04-30 10:44:56] local.ERROR: Error happend :( {"id":"12345","username":"some_guy"}

Step 4 — Building Log Stack

As mentioned in the Channel drivers section, stack driver allows us to stack multiple channels together and send one message to all the channels at once. We can make a stack of multiple channels where each has a different driver therefore we can create a simple hierarchy. Let's take a look at the following example:

'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['debug-channel','error-channel'],
            'ignore_exceptions' => false,
        ],

        'debug-channel' => [
            'driver' => 'single',
            'path' => storage_path('logs/debug.log'),
            'level' => env('LOG_LEVEL', 'error'),
        ],

        'error-channel' => [
            'driver' => 'single',
            'path' => storage_path('logs/error.log'),
            'level' => env('LOG_LEVEL', 'error'),
        ],
]

In the code above, we have created a stack of channels consisting of debug-channel and error-channel.

The debug-channel sends messages to the storage/logs/debug.log file and its level is set to debug. That means that every log message sent to this stack will be logged to this file.

On the other hand, the error-channel logs only messages that have an error level or higher and logs them to the storage/logs/error.log file.

Channels with single or daily drivers have an optional configuration option called bubble. This option is by default set to true and indicates if messages should bubble up to other channels after being handled.

Now, let's take a look at the following code:

use Illuminate\\Support\\Facades\\Log;

Log::debug("Just a debug message ...");

try{
    throw new Exception('Something terrible happened!');
}
catch(Exception $e){
    Log::error($e->getMessage());
}

In this code, we sent debug level message to the stack. As it's a debug level message and the bubble option is by default set to true, the message passed through both channels.

Then, we threw an exception. This exception was handled in the catch block and its message was sent to the stack channel.

As it's an error level message, it didn't pass through the debug-channel, but it passed through the error-channel.

Code above created two entries in the storage/logs/debug.log file:

Output:
[2021-04-30 10:20:00] local.DEBUG: Just a debug message ...  
[2021-04-30 10:20:00] local.ERROR: Something terrible happened!

And one entry in the storage/logs/error.log file:

Output:
[2021-04-30 10:20:00] local.ERROR: Something terrible happened!

Step 5 — Customizing Monolog Channels

Creating Monolog Handler Channels

Laravel doesn't include a channel driver for each of the Monolgs handlers. Sometimes, when Laravel doesn't have a corresponding driver you may want to create an instance of a specific Monolog handler. A channel like that can be created using the monolog driver.

When using the monolog driver, the handler option is used to specify which of the Monologs handlers you want to use. As this option creates a new instance of this handler, you may want to provide the constructor with the specific parameters. These parameters are provided by the with option where you specify the name and the value of the parameter.

Let's say you want to create an instance of NativeMailerhandler. Then you can create a channel that may look like this:

'native-mail-channel' => [
    'driver'  => 'monolog',
    'handler' => Monolog\\Handler\\NativeMailerHandler::class,
    'with' => [
        'to' => 'webadmin@example.com',
        'subject' => 'Fatal run-time error',
				'from' => 'info@example.com',
    ],
],

The full list of all Monolog handlers can be found in the official Monolog documentation.

Using Monolog Formatters

Monolog has multiple built-in formatters that are used to transform your log message to the desired format. A full list of the built-in formatters can be found on the official GitHub documentation.

By default, every channel with the monolog driver uses LineFormatter. You can change that using the formatter and formatter_with options.

'browser-output' => [
    'driver' => 'monolog',
    'handler' => Monolog\\Handler\\BrowserConsoleHandler::class,
    'formatter' => Monolog\\Formatter\\HtmlFormatter::class,
    'formatter_with' => [
        'dateFormat' => 'Y-m-d',
    ],
],

If you are using a handler that comes with its own formatter, you can set the formatter option to default.


Step 6 — Creating Custom Channel Using Factories

You can also create a completely custom channel with the custom driver and have complete control over Monolog's instantiation. In this case, your configuration has to include via option where you specify which factory class will be invoked to create the Monolog instance.

'example-custom-channel' => [
    'driver' => 'custom',
    'via' => App\\Logging\\MyCustomLogger::class,
],

The factory class only needs a __invoke() method which should return the Monolog logger instance and takes the configuration array as its argument.

namespace App\\Logging;

use Monolog\\Logger;

class MyCustomLogger{
    /**
     * Create a custom Monolog instance.
     *
     * @param  array  $config
     * @return \\Monolog\\Logger
     */
    public function __invoke(array $config){
				//Setting up the logger
				// ...

				//Returning logger isntance
        return new Logger(...);
    }
}

Conclusion

In this tutorial, you have learned how logging in Laravel works. You have learned how to create logging channels to send messages into, how to create channel stacks to send messages to multiple channels at once, how to customize Monolog channels, and how to create completely custom channels. Now, you can create a powerful logging system in your Laravel project.