How to add data to all log records in Laravel?

2020-06-16 10:05发布

问题:

I would like to add some data to all log records within my Laravel application.

I think it would be helpful to know the username of the current user and/or the client IP address.

Currently I'm adding it manually by doing:

Log::info('Pre-paid activation.', array('username' => Auth::user()->username));

But I would like to know how to do add a listener or something to make all log recorde have the username (if available).

回答1:

Since Laravel comes out of box with Monolog, it is pretty straight forward. It can be easily achieved by editing app/start/global.php and add the following after the line that starts with: Log::useFiles:

Log::useFiles(storage_path().'/logs/laravel.log');
$monolog = Log::getMonolog();
$monolog->pushProcessor(function ($record) {
    $record['extra']['user'] = Auth::user() ? Auth::user()->username : 'anonymous';
    $record['extra']['ip'] = Request::getClientIp();
    return $record;
});

Basically, we are using the underlying Monolog instance to register a processor which will intercept any log record to be written. The results will be similar as the followings:

[2014-04-12 23:07:35] local.INFO: Pre-paid activation. [] {"user":"anonymous","ip":"::1"}

More information about Monolog processors: https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md#using-processors


Extra: The hardcoded extra is to tell Monolog to add data as extra piece of information (redundant to say). In the practice, this is to avoid overriding any context data added in the original log call.



回答2:

In Laravel 5 you can do it by customising Monolog. First create a class which customises Monolog so it adds the user information to the log records:

namespace App\Logging;

use App\Models\User;
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
use Illuminate\Log\Logger;

class AddUserInformation
{
    protected $request;

    public function __construct(Request $request = null)
    {
        $this->request = $request;
    }

    public function __invoke(Logger $logger)
    {
        if ($this->request) {
            foreach ($logger->getHandlers() as $handler) {
                $handler->pushProcessor([$this, 'processLogRecord']);
            }
        }
    }

    public function processLogRecord(array $record): array
    {
        $record['extra'] += [
            'user' => $this->request->user()->username ?? 'guest',
            'ip' => $this->request->getClientIp()
        ];

        return $record;
    }
}

Then connect the class to the Laravel logger by adding it to the channel setup in the config/logging.php file:

// ...
'channels' => [
    'single' => [
        'driver' => 'single',
        'tap' => [\App\Logging\AddUserInformation::class],
        'path' => storage_path('logs/laravel.log'),
        'level' => env('LOG_LEVEL', 'debug'),
    ],
    // ...
]

A documentation about customising Monolog



回答3:

For those looking for laravel 7.x it's explained here in the official doc https://laravel.com/docs/7.x/logging#customizing-monolog-for-channels

edit config\logging.php and add this to the channel you want to log to:

use Illuminate\Support\Facades\App;
...
'stack' => [
...
   'tap' => [App\Logging\CustomizeFormatter::class],
],

then create file App\Logging\CustomizeFormatter.php

<?php

namespace App\Logging;

use Monolog\Formatter\LineFormatter;

class CustomizeFormatter
{
    public function __invoke($logger)
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(new LineFormatter('custom logging - %level_name% %message% %context% %extra%'));
        }
    }
}