w3resource

Laravel (5.7) Logging

Errors

A project while underway, is borne to have a few errors. Errors and exception handling is already configured for you when you start a new Laravel project. Normally, in a local environment we need to see errors for debugging purposes. We need to hide these errors from users in production environment. This can be achieved with the variable APP_DEBUG set in the environment file .env stored at the root of the application.

For local environment the value of APP_DEBUG should be true but for production it needs to be set to false to hide errors.

Note − After changing the APP_DEBUG variable, you should restart the Laravel server.

Logging

Logging is an important mechanism by which the system can log errors that are generated. It is useful to improve the reliability of the system. Laravel supports different logging modes like single, daily, syslog, and errorlog modes. You can set these modes in config/app.php file.

'log' => 'daily'

You can see the generated log entries in storage/logs/laravel.log file.

Laravel has this philosophy about using popular open source libraries to fulfill various common features required in a typical web framework: Logging is a common feature. Hence, by default, Laravel employsMonolog, a highly popular PHP logging library, for all its logging needs. The wonderful thing about Monolog is that it provides a common interface for you to write logs to anything, from standard text files to third-party log management services.

Monolog is highly flexible. It can send your logs to files, sockets, email, databases, and various web services. I will show you in a later section how Monolog provides handlers that easily help you to send logs to these various destinations. Laravel typically sets up Monolog via a logging configuration file. Now I’m going to show you how the logging configuration file works.

Exploring the logging config file

In Laravel, there are various configuration files found in the config folder. This folder holds config files meant for database connections, email connections, caching, etc. You should expect your logging configuration also to be found here, and it’s path will be in config/logging.php.

Imports

Laravel Imports.

When you create a Laravel app, the first few lines are the imports it uses. By default, you should see the two handlers shown above being imported. As explained in the earlier section, these are two of the typical handlers that Monolog provides.

Channels

Laravel Monolog uses a concept called channels. The channels are the different places where you can send your logs. For the rest of the config/logging.php, the config file returns an associative array with two main keys — default and channels.

Laravel Channels.

Default represents the default channel that any logging is sent to. It's crucial that the channel you pick as default is represented in the array under the key channels. As you can see above, stack is the default channel.

Laravel Channels - 2.

Channels represent the full list of channels available for sending your logs, and stack is the first channel listed. For the rest of this article, whenever I mention channel list, I'm referring specifically to this list.

Configuration within each channel

In each channel stated under the channel list, you can see different keys being used. Knowing how each key affects logging will give you the maximum flexibility to configure the logging output you want.

The first type I want to cover is drivers. Before Laravel 5.6, Laravel only had drivers supporting four file-based outputs:

  1. Writing to a single file. By default, that’s usually storage/logs/laravel.log.
  2. Daily files. By default, that will give you files like
    storage/logs/laravel-2018-12-03.log,
    storage/logs/laravel-2018-12-04.log
    , and so on.
  3. Error log. The location of the error log depends on the web server software you are using, such as nginx or Apache, and the server OS. Typically this means that on Linux with Nginx, the file is at /var/log/nginx/error.log.
  4. System log, also known as syslog, and once again, the location depends on the server OS.

Drivers

With Laravel 5.6 and above, we have brand new drivers supporting a wider range of logging outputs. The old drivers are still available under the values"single," "daily," "errorlog," and "syslog." Now we have new values for new kinds of logging outputs. Some commonly used new drivers include:

Stack

This simply means you can stack multiple logging channels together. When you use the "stack" driver, you need to set an array of values for other channels under the key "channels." See Fig 4 for the example of a stack driver in use.

Monolog

It's a bit weird to see Monolog show up again, this time as its own driver. The point to remember is that when you want to use Monolog's native handlers, then you want to use the Monolog driver. Thankfully, the default logging config file provides two examples of using two different Monolog handlers. Instead of going into too much detail on the various Monolog handler types, I will leave you with a link to the full list of Monolog handlers so you can review at your own speed.

Laravel Monolog.

Custom

There’s only a small write-up about this in the official Laravel documentation. Between the popular legacy drivers and the various Monolog handlers, it’s pretty hard to imagine someone writing up their own custom channel driver.

Nevertheless, if that’s something you want to do, you can. Usually, a custom channel is for you to write logs to third-party services, like Apache Kafka and Logstash.

First, your custom channel needs to choose a custom driver and then add a via option to point to a logger factory class.

'channels' => [
    'custom' => [
        'driver' => 'custom',
        'via' => AppLoggingCustomLoggerFactory::class,
    ],  
],

After you setup the custom channel, you’re ready to define the factory class. Bear in mind that you must return a Monolog Logger instance via the __invoke method.

<?php
namespace AppLogging;
use MonologLogger;

class CustomLoggerFactory
{
    /**
    * This class will create a custom Monolog instance.
    *
    * @param array $config
    * @return MonologLogger
    */
    public function __invoke(array $config)
    {
        return new Logger(...);
    }
}

Try to use the custom driver as the last resort. If you search around a bit, usually you'll find a standardized way to use existing drivers or handlers for your special logging needs. Even if you have to write your own custom logger driver, try to see if somebody has already written an open source version or an example of the Logger factory class you need. In other words: don't reinvent the wheel if you can help it.

Handlers

The next important thing you need to learn after channels and drivers are the concept of handlers. There is a complete list of handlers for Monolog to send logs to. You can even employ special handlers so you can build more advanced logging strategies. To save space, I will leave it to you to read the full list of handlers on your own and simply group Monolog’s wide range of handlers into the following types:

  • Logging to files and Syslog
  • Sending alerts (such as Slack) and emails
  • Logging to specific servers and networked logging
  • Logging during development (including sending logs to ChromePHP extension)
  • Sending logs to databases
  • Special handlers

Also, note that there’s a special option called handler_with that allows you to assign values to the individual handler’s constructor method.

public function __construct(string $ident, $facility = LOG_USER, $level = Logger::DEBUG, bool $bubble = true, int $logopts = LOG_PID)

At the individual channel configuration, you can assign the values for the parameters of the constructor this way.

'syslog_channel_name' => [
    'driver' => 'monolog',
    'handler' => MonologHandlerSyslogHandler::class,
    'handler_with' => [
        'ident' => 'some value you want to assign to the ident parameter',
    ],
],

Writing log messages and levels

If you’ve made it this far, congratulations! You have now gone deep into the details of the configuration file for Laravel logging. Most developers have only a cursory understanding of this and didn’t let that stop them from writing log messages. Therefore, you are now more equipped than most developers at configuring Laravel logging for your team.

One key configuration detail I didn’t cover in the previous section levels. This is partly because the previous section was already long enough, but mainly because it makes more sense to cover levels here.

Levels of logging

At the specific file that you want to write log messages, ensure that you import the Log facade before writing out your log message, like this:

use IlluminateSupportFacadesLog;
//.. and then somewhere in your php file
Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($message);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);

Typically developers want to have different levels of severity when it comes to log messages. The different log levels stated here correspond to the RFC-5424 specification. So the levels and their names are not decided willy-nilly. These levels have an order of priority with the highest being emergency and the lowest being debug. At the channels list, you can specify the minimum level the individual channel will log any messages. In the case of Fig. 5, for example, this means the Slack channel will write any log messages with a level of critical and above when triggered.

Log facade sends to the default channel

The level’s not the only factor that determines if Laravel will eventually write a specific message to the channel. The channel as a destination is another factor as well. Bear in mind when you use the Log facade to write log messages, Laravel will send these to the default channel stated in your config/logging.php.

If you leave the logging config file unchanged, and assuming you didn't set the LOG_CHANNEL environment variable to a different value, your default channel will be the stack "multi-channel," which in turn consists of just the "daily" channel as seen in Figs 3 and 4.

Sending log messages to alternative channels on the fly

This then begs the question: what if you want to send a specific message to a channel other than the default channel? You can deliberately choose an alternative channel on the fly this way:

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

Remember to make sure that the alternative channel you choose exists in the channel list within the logging config file. Otherwise, you'll encounter errors. In the example above, this means you need to declare a channel explicitly in the channel list called suspicious.

And if you want to send the same logging message to multiple channels, you can also perform "stacking."

Log::stack(['suspicious', 'slack'])->info("I have a bad feeling about this!");

Sending contextual information

Sometimes you want to send contextual information, such as the specific file name, the line in the file, or even the current logged-in user.

You can write your individual log messages this way to add contextual information:

Log::info('User login failed.', ['id' => $user->id, 'file' => __FILE__, 'line' => __LINE__]);

This command will pass in an array of contextual data to the log methods. Laravel will then format the contextual data and display it along with the log message.

Formatting log messages

This section on formatting could have been placed alongside the drivers and handlers section under "Explaining the logging config file." But I prefer to have "Formatting log messages" be its own standalone section, coming after everything else, because it tends to be less crucial.

The main reason formatting is often less crucial is that you can get away with the default formatter that Monolog driver uses—the Monolog LineFormatter. That's usually more than good enough. However, there may be times when you wish to customize the formatter passed to the handler. This is a full list of formatters that Monolog natively provides. I have noticed that Monolog arranges this list by the highest frequency of usage. So it’s a good list to reference should you want to select alternatives.

When you want to customize the formatter, you need to set the formatter option under the specific channel configuration. You will also notice the formatter_with option. It works in the similar way as the handler_with option explained in the "Handlers" section. This is a way to send values to the formatter class constructor method. Here's an example where I use the next most popular formatter, the HtmlFormatter:

'browser_console' => [
    'driver' => 'monolog',
    'handler' => MonologHandlerBrowserConsoleHandler::class,
    'formatter' => MonologFormatterHtmlFormatter::class,
    'formatter_with' => [
        'dateFormat' => 'Y-m-d',
    ],
],
>

Sometimes a particular Monolog handler comes with its own formatter. In that case, you can simply instruct the formatter to use that handler's default formatter.

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:

'newrelic' => [
    'driver' => 'monolog',
    'handler' => MonologHandlerNewRelicHandler::class,
    'formatter' => 'default', 
],

Previous: Laravel (5.7) Error Handling
Next: Laravel (5.7) Blade Template



Follow us on Facebook and Twitter for latest update.