By default, Laravel aggregates all log messages into one file which is in storage/logs/laravel.log relative to the root of the project. In this tutorial, we'll go over how to create custom log files in Laravel based on different configurations, for example, different message levels.

This tutorial uses Laravel 8 and PHP 7.3.


If you already have Laravel installed, you can skip this step. If not, follow along.

First, download Composer if you don't have it on your system.

Then, run the following command to create the Laravel project:

composer create-project laravel/laravel custom-logs

This command will download Laravel and all its dependencies, and copy .env.sample to .env.

Now change the directory to the project and start the server:

cd custom-logs
php artisan serve

If you go to localhost:8000 (or whatever port Laravel uses after running the above command), you'll see that Laravel is working.

If you get an error about permissions, change the permission of the storage directory to be readable.

Understanding Laravel's Logs

Laravel has different channels for logging. Channels are different ways to log a message. For example, the slack channel will log the messages to Slack, whereas the single channel just puts all messages in one file (by default, at storage/logs/laravel.log). Laravel handles logs using the Monolog library.

The configuration for Laravel's logs are found in config/logging.php. There, you'll find the default channel:

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

By default, Laravel uses the stack channel, which allows you to use many channels at once. We will get into how it works later in this tutorial.

You'll also find an array of channels each having a similar structure:

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

Each channel definition has the following configurations:

  1. driver: which driver it uses. It can be one of the default drivers like single or slack, or it can be a custom driver you create yourself.
  2. path: The path to the log file the output is in if there's any.
  3. level: the minimum level the logged message should have to be handled by this channel.
  4. bubble: should the logged message bubble to other channels after being handled by this channel.
  5. locking: Should the log file be locked before writing to it.
  6. permission: The permission that the log file should have when it's created.

There are other configurations as well depending on the channel driver. For example, the slack driver takes username as a configuration.

Understanding Levels

The levels of the log messages can be emergency, alert, critical, error, warning, notice, info, and debug (sorted from most critical to the least critical). When you assign a level for a channel, it will only log messages of that level or higher. For example, a channel that has the level set to "warning" will only log messages of level warning, error, critical, alert, or emergency. Anything below "warning" (notice, info, and debug) will not be handled by this channel.

How to Log Messages

On your website, you can use the Log facade to log messages of different levels:

use Illuminate\Support\Facades\Log;


The Log facade will use the default channel you chose in config/logging.php. So, you only need to make configuration changes there without having to do it every time you want to log a message.

Multiple Files Based On Levels

In this section, we'll see how to create different files depending on the level of the message. We'll log all messages having the level "error" or higher into storage/logs/errors.log, and all messages having a lower level than that into storage/logs/debug.log. This way, it will be easier to track errors in our Laravel project and separate them from debug messages that might not be as important.

First, add to the channels array in config/logging.php a new channel custom_errors with the following configuration:

'custom_error' => [
    'driver' => 'single',
    'level'  => 'error',
    'path'   => storage_path('logs/errors.log'),
    'bubble' => false

This channel uses the single driver, meaning that it will aggregate all the messages in one file. That file will be storage/logs/errors.log as defined in the path configuration of the channel. We've set the level to error, and we've set bubble to false. This means that once the message is logged by this channel, the message will not propagate to any of the other channels.

Next, add another new channel custom_debug with the following configuration:

'custom_debug' => [
    'driver' => 'single',
    'level'  => 'debug',
    'path'   => storage_path('logs/debug.log'),

This channel also uses the single drive, as it also aggregates the messages into one file, the file being storage/logs/debug.log as defined by the path configuration. Its level is set to debug, meaning it will log any message having the level debug or higher.

What's left is to make sure our default channel uses these newly created channels. Go to the stack channel in the channels array, which is usually the first item in the array. It should look something like this:

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

It uses the stack driver, which as mentioned earlier allows using multiple channels. These channels can be set in the channels configuration, which by default is an array having one item single. For that reason Laravel by default logs all messages in storage/logs/laravel.log.

What we need to do is change the channels configuration of the stack channel to use the custom logs we created earlier:

'stack' => [
    'driver' => 'stack',
    'channels' => ['custom_error', 'custom_debug'],
    'ignore_exceptions' => false,

One important detail is that custom_error precedes custom_debug in the array. As custom_debug has the level debug, it can actually log all levels of messages, including error messages. We only want it to log messages having a level lower than "error", so, we give custom_error a higher priority, as it only logs messages having a level "error" or higher and will prevent the message from bubbling since the bubble configuration is set to false.

Let's test it out. If you're using a fresh installation of Laravel, you can just go to routes/web.php and add the following in the main route available:

use Illuminate\Support\Facades\Log;

Route::get('/', function () {
    Log::info("this is info message");
    Log::error("This is error message");
    return view('welcome');

You can add the same lines to any other route or controller:

//inside controller or route
 Log::info("this is info message");
 Log::error("This is error message");

Then, navigate to the route you added the code to. In the first case, it will be localhost:8000. After that, let's check if the messages were logged in the correct files.

At the root of your project, check the files that are currently in storage/logs:

ls storage/logs

You should see debug.log and errors.log among other files (if there are any others).

Let's check the content of debug.log:

cat storage/logs/debug.log

And it should have the following:

[TIME] local.INFO: this is info message

Where TIME is the date and time this message is logged. Notice that only the message having the level "info" is logged, meaning it was handled by the "custom_debug" channel.

Next, let's check the content of errors.log:

cat storage/logs/errors.log

It should have the following:

[TIME] local.ERROR: This is error message

Where TIME is the date and time this message is logged. Notice that only the message having the level "error" is logged, meaning it was handled by the "custom_errors" channel.

So, our configuration was successful! We will now have 2 separate logs, one for debug messages and one for error messages.


Customizing how logs are handled can be pretty easy with Laravel. Other than the example we made, you can try to also use a different driver for your log channel like slack or daily, and you can add as many channels as you want to the stack channel.