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.
Installation
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:
driver
: which driver it uses. It can be one of the default drivers likesingle
orslack
, or it can be a custom driver you create yourself.path
: The path to the log file the output is in if there's any.level
: the minimum level the logged message should have to be handled by this channel.bubble
: should the logged message bubble to other channels after being handled by this channel.locking
: Should the log file be locked before writing to it.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;
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 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.
Conclusion
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.