Logging

Introduction

To help you learn more about what's happening within your application, Laravel provides robust logging services that allow you to log messages to files, the system error log, and even to Slack to notify your entire team.

Laravel logging is based on "channels". Each channel represents a specific way of writing log information. For example, the single channel writes log files to a single log file, while the slack channel sends log messages to Slack. Log messages may be written to multiple channels based on their severity.

Under the hood, Laravel utilizes the Monolog library, which provides support for a variety of powerful log handlers. Laravel makes it a cinch to configure these handlers, allowing you to mix and match them to customize your application's log handling.

Configuration

All of the configuration options for your application's logging behavior is housed in the config/logging.php configuration file. This file allows you to configure your application's log channels, so be sure to review each of the available channels and their options. We'll review a few common options below.

By default, Laravel will use the stack channel when logging messages. The stack channel is used to aggregate multiple log channels into a single channel. For more information on building stacks, check out the documentation below.

Configuring The Channel Name

By default, Monolog is instantiated with a "channel name" that matches the current environment, such as production or local. To change this value, add a name option to your channel's configuration:

1'stack' => [
2 'driver' => 'stack',
3 'name' => 'channel-name',
4 'channels' => ['single', 'slack'],
5],
1'stack' => [
2 'driver' => 'stack',
3 'name' => 'channel-name',
4 'channels' => ['single', 'slack'],
5],

Available Channel Drivers

Each log channel is powered by a "driver". The driver determines how and where the log message is actually recorded. The following log channel drivers are available in every Laravel application. An entry for most of these drivers is already present in your application's config/logging.php configuration file, so be sure to review this file to become familiar with its contents:

NameDescription
customA driver that calls a specified factory to create a channel
dailyA RotatingFileHandler based Monolog driver which rotates daily
errorlogAn ErrorLogHandler based Monolog driver
monologA Monolog factory driver that may use any supported Monolog handler
nullA driver that discards all log messages
papertrailA SyslogUdpHandler based Monolog driver
singleA single file or path based logger channel (StreamHandler)
slackA SlackWebhookHandler based Monolog driver
stackA wrapper to facilitate creating "multi-channel" channels
syslogA SyslogHandler based Monolog driver
lightbulb

Check out the documentation on advanced channel customization to learn more about the monolog and custom drivers.

Channel Prerequisites

Configuring The Single and Daily Channels

The single and daily channels have three optional configuration options: bubble, permission, and locking.

NameDescriptionDefault
bubbleIndicates if messages should bubble up to other channels after being handledtrue
lockingAttempt to lock the log file before writing to itfalse
permissionThe log file's permissions0644

Configuring The Papertrail Channel

The papertrail channel requires the host and port configuration options. You can obtain these values from Papertrail.

Configuring The Slack Channel

The slack channel requires a url configuration option. This URL should match a URL for an incoming webhook that you have configured for your Slack team.

By default, Slack will only receive logs at the critical level and above; however, you can adjust this in your config/logging.php configuration file by modifying the level configuration option within your Slack log channel's configuration array.

Logging Deprecation Warnings

PHP, Laravel, and other libraries often notify their users that some of their features have been deprecated and will be removed in a future version. If you would like to log these deprecation warnings, you may specify your preferred deprecations log channel in your application's config/logging.php configuration file:

1'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
2 
3'channels' => [
4 ...
5]
1'deprecations' => env('LOG_DEPRECATIONS_CHANNEL', 'null'),
2 
3'channels' => [
4 ...
5]

Or, you may define a log channel named deprecations. If a log channel with this name exists, it will always be used to log deprecations:

1'channels' => [
2 'deprecations' => [
3 'driver' => 'single',
4 'path' => storage_path('logs/php-deprecation-warnings.log'),
5 ],
6],
1'channels' => [
2 'deprecations' => [
3 'driver' => 'single',
4 'path' => storage_path('logs/php-deprecation-warnings.log'),
5 ],
6],

Building Log Stacks

As mentioned previously, the stack driver allows you to combine multiple channels into a single log channel for convenience. To illustrate how to use log stacks, let's take a look at an example configuration that you might see in a production application:

1'channels' => [
2 'stack' => [
3 'driver' => 'stack',
4 'channels' => ['syslog', 'slack'],
5 ],
6 
7 'syslog' => [
8 'driver' => 'syslog',
9 'level' => 'debug',
10 ],
11 
12 'slack' => [
13 'driver' => 'slack',
14 'url' => env('LOG_SLACK_WEBHOOK_URL'),
15 'username' => 'Laravel Log',
16 'emoji' => ':boom:',
17 'level' => 'critical',
18 ],
19],
1'channels' => [
2 'stack' => [
3 'driver' => 'stack',
4 'channels' => ['syslog', 'slack'],
5 ],
6 
7 'syslog' => [
8 'driver' => 'syslog',
9 'level' => 'debug',
10 ],
11 
12 'slack' => [
13 'driver' => 'slack',
14 'url' => env('LOG_SLACK_WEBHOOK_URL'),
15 'username' => 'Laravel Log',
16 'emoji' => ':boom:',
17 'level' => 'critical',
18 ],
19],

Let's dissect this configuration. First, notice our stack channel aggregates two other channels via its channels option: syslog and slack. So, when logging messages, both of these channels will have the opportunity to log the message. However, as we will see below, whether these channels actually log the message may be determined by the message's severity / "level".

Log Levels

Take note of the level configuration option present on the syslog and slack channel configurations in the example above. This option determines the minimum "level" a message must be in order to be logged by the channel. Monolog, which powers Laravel's logging services, offers all of the log levels defined in the RFC 5424 specification: emergency, alert, critical, error, warning, notice, info, and debug.

So, imagine we log a message using the debug method:

1Log::debug('An informational message.');
1Log::debug('An informational message.');

Given our configuration, the syslog channel will write the message to the system log; however, since the error message is not critical or above, it will not be sent to Slack. However, if we log an emergency message, it will be sent to both the system log and Slack since the emergency level is above our minimum level threshold for both channels:

1Log::emergency('The system is down!');
1Log::emergency('The system is down!');

Writing Log Messages

You may write information to the logs using the Log facade. As previously mentioned, the logger provides the eight logging levels defined in the RFC 5424 specification: emergency, alert, critical, error, warning, notice, info and debug:

1use Illuminate\Support\Facades\Log;
2 
3Log::emergency($message);
4Log::alert($message);
5Log::critical($message);
6Log::error($message);
7Log::warning($message);
8Log::notice($message);
9Log::info($message);
10Log::debug($message);
1use Illuminate\Support\Facades\Log;
2 
3Log::emergency($message);
4Log::alert($message);
5Log::critical($message);
6Log::error($message);
7Log::warning($message);
8Log::notice($message);
9Log::info($message);
10Log::debug($message);

You may call any of these methods to log a message for the corresponding level. By default, the message will be written to the default log channel as configured by your logging configuration file:

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use App\Models\User;
7use Illuminate\Support\Facades\Log;
8 
9class UserController extends Controller
10{
11 /**
12 * Show the profile for the given user.
13 *
14 * @param int $id
15 * @return \Illuminate\Http\Response
16 */
17 public function show($id)
18 {
19 Log::info('Showing the user profile for user: '.$id);
20 
21 return view('user.profile', [
22 'user' => User::findOrFail($id)
23 ]);
24 }
25}
1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Http\Controllers\Controller;
6use App\Models\User;
7use Illuminate\Support\Facades\Log;
8 
9class UserController extends Controller
10{
11 /**
12 * Show the profile for the given user.
13 *
14 * @param int $id
15 * @return \Illuminate\Http\Response
16 */
17 public function show($id)
18 {
19 Log::info('Showing the user profile for user: '.$id);
20 
21 return view('user.profile', [
22 'user' => User::findOrFail($id)
23 ]);
24 }
25}

Contextual Information

An array of contextual data may be passed to the log methods. This contextual data will be formatted and displayed with the log message:

1use Illuminate\Support\Facades\Log;
2 
3Log::info('User failed to login.', ['id' => $user->id]);
1use Illuminate\Support\Facades\Log;
2 
3Log::info('User failed to login.', ['id' => $user->id]);

Occasionally, you may wish to specify some contextual information that should be included with all subsequent log entries. For example, you may wish to log a request ID that is associated with each incoming request to your application. To accomplish this, you may call the Log facade's withContext method:

1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Support\Facades\Log;
7use Illuminate\Support\Str;
8 
9class AssignRequestId
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Illuminate\Http\Request $request
15 * @param \Closure $next
16 * @return mixed
17 */
18 public function handle($request, Closure $next)
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::withContext([
23 'request-id' => $requestId
24 ]);
25 
26 return $next($request)->header('Request-Id', $requestId);
27 }
28}
1<?php
2 
3namespace App\Http\Middleware;
4 
5use Closure;
6use Illuminate\Support\Facades\Log;
7use Illuminate\Support\Str;
8 
9class AssignRequestId
10{
11 /**
12 * Handle an incoming request.
13 *
14 * @param \Illuminate\Http\Request $request
15 * @param \Closure $next
16 * @return mixed
17 */
18 public function handle($request, Closure $next)
19 {
20 $requestId = (string) Str::uuid();
21 
22 Log::withContext([
23 'request-id' => $requestId
24 ]);
25 
26 return $next($request)->header('Request-Id', $requestId);
27 }
28}

Writing To Specific Channels

Sometimes you may wish to log a message to a channel other than your application's default channel. You may use the channel method on the Log facade to retrieve and log to any channel defined in your configuration file:

1use Illuminate\Support\Facades\Log;
2 
3Log::channel('slack')->info('Something happened!');
1use Illuminate\Support\Facades\Log;
2 
3Log::channel('slack')->info('Something happened!');

If you would like to create an on-demand logging stack consisting of multiple channels, you may use the stack method:

1Log::stack(['single', 'slack'])->info('Something happened!');
1Log::stack(['single', 'slack'])->info('Something happened!');

On-Demand Channels

It is also possible to create an on-demand channel by providing the configuration at runtime without that configuration being present in your application's logging configuration file. To accomplish this, you may pass a configuration array to the Log facade's build method:

1use Illuminate\Support\Facades\Log;
2 
3Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6])->info('Something happened!');
1use Illuminate\Support\Facades\Log;
2 
3Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6])->info('Something happened!');

You may also wish to include an on-demand channel in an on-demand logging stack. This can be achieved by including your on-demand channel instance in the array passed to the stack method:

1use Illuminate\Support\Facades\Log;
2 
3$channel = Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6]);
7 
8Log::stack(['slack', $channel])->info('Something happened!');
1use Illuminate\Support\Facades\Log;
2 
3$channel = Log::build([
4 'driver' => 'single',
5 'path' => storage_path('logs/custom.log'),
6]);
7 
8Log::stack(['slack', $channel])->info('Something happened!');

Monolog Channel Customization

Customizing Monolog For Channels

Sometimes you may need complete control over how Monolog is configured for an existing channel. For example, you may want to configure a custom Monolog FormatterInterface implementation for Laravel's built-in single channel.

To get started, define a tap array on the channel's configuration. The tap array should contain a list of classes that should have an opportunity to customize (or "tap" into) the Monolog instance after it is created. There is no conventional location where these classes should be placed, so you are free to create a directory within your application to contain these classes:

1'single' => [
2 'driver' => 'single',
3 'tap' => [App\Logging\CustomizeFormatter::class],
4 'path' => storage_path('logs/laravel.log'),
5 'level' => 'debug',
6],
1'single' => [
2 'driver' => 'single',
3 'tap' => [App\Logging\CustomizeFormatter::class],
4 'path' => storage_path('logs/laravel.log'),
5 'level' => 'debug',
6],

Once you have configured the tap option on your channel, you're ready to define the class that will customize your Monolog instance. This class only needs a single method: __invoke, which receives an Illuminate\Log\Logger instance. The Illuminate\Log\Logger instance proxies all method calls to the underlying Monolog instance:

1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Formatter\LineFormatter;
6 
7class CustomizeFormatter
8{
9 /**
10 * Customize the given logger instance.
11 *
12 * @param \Illuminate\Log\Logger $logger
13 * @return void
14 */
15 public function __invoke($logger)
16 {
17 foreach ($logger->getHandlers() as $handler) {
18 $handler->setFormatter(new LineFormatter(
19 '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
20 ));
21 }
22 }
23}
1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Formatter\LineFormatter;
6 
7class CustomizeFormatter
8{
9 /**
10 * Customize the given logger instance.
11 *
12 * @param \Illuminate\Log\Logger $logger
13 * @return void
14 */
15 public function __invoke($logger)
16 {
17 foreach ($logger->getHandlers() as $handler) {
18 $handler->setFormatter(new LineFormatter(
19 '[%datetime%] %channel%.%level_name%: %message% %context% %extra%'
20 ));
21 }
22 }
23}
lightbulb

All of your "tap" classes are resolved by the service container, so any constructor dependencies they require will automatically be injected.

Creating Monolog Handler Channels

Monolog has a variety of available handlers and Laravel does not include a built-in channel for each one. In some cases, you may wish to create a custom channel that is merely an instance of a specific Monolog handler that does not have a corresponding Laravel log driver. These channels can be easily created using the monolog driver.

When using the monolog driver, the handler configuration option is used to specify which handler will be instantiated. Optionally, any constructor parameters the handler needs may be specified using the with configuration option:

1'logentries' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\SyslogUdpHandler::class,
4 'with' => [
5 'host' => 'my.logentries.internal.datahubhost.company.com',
6 'port' => '10000',
7 ],
8],
1'logentries' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\SyslogUdpHandler::class,
4 'with' => [
5 'host' => 'my.logentries.internal.datahubhost.company.com',
6 'port' => '10000',
7 ],
8],

Monolog Formatters

When using the monolog driver, the Monolog LineFormatter will be used as the default formatter. However, you may customize the type of formatter passed to the handler using the formatter and formatter_with configuration options:

1'browser' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\BrowserConsoleHandler::class,
4 'formatter' => Monolog\Formatter\HtmlFormatter::class,
5 'formatter_with' => [
6 'dateFormat' => 'Y-m-d',
7 ],
8],
1'browser' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\BrowserConsoleHandler::class,
4 'formatter' => Monolog\Formatter\HtmlFormatter::class,
5 'formatter_with' => [
6 'dateFormat' => 'Y-m-d',
7 ],
8],

If you are using a Monolog handler that is capable of providing its own formatter, you may set the value of the formatter configuration option to default:

1'newrelic' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\NewRelicHandler::class,
4 'formatter' => 'default',
5],
1'newrelic' => [
2 'driver' => 'monolog',
3 'handler' => Monolog\Handler\NewRelicHandler::class,
4 'formatter' => 'default',
5],

Creating Custom Channels Via Factories

If you would like to define an entirely custom channel in which you have full control over Monolog's instantiation and configuration, you may specify a custom driver type in your config/logging.php configuration file. Your configuration should include a via option that contains the name of the factory class which will be invoked to create the Monolog instance:

1'channels' => [
2 'example-custom-channel' => [
3 'driver' => 'custom',
4 'via' => App\Logging\CreateCustomLogger::class,
5 ],
6],
1'channels' => [
2 'example-custom-channel' => [
3 'driver' => 'custom',
4 'via' => App\Logging\CreateCustomLogger::class,
5 ],
6],

Once you have configured the custom driver channel, you're ready to define the class that will create your Monolog instance. This class only needs a single __invoke method which should return the Monolog logger instance. The method will receive the channels configuration array as its only argument:

1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Logger;
6 
7class CreateCustomLogger
8{
9 /**
10 * Create a custom Monolog instance.
11 *
12 * @param array $config
13 * @return \Monolog\Logger
14 */
15 public function __invoke(array $config)
16 {
17 return new Logger(...);
18 }
19}
1<?php
2 
3namespace App\Logging;
4 
5use Monolog\Logger;
6 
7class CreateCustomLogger
8{
9 /**
10 * Create a custom Monolog instance.
11 *
12 * @param array $config
13 * @return \Monolog\Logger
14 */
15 public function __invoke(array $config)
16 {
17 return new Logger(...);
18 }
19}

Comments

No Comments Yet

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.