laravel 5.2自定义日志文件,用于不同的任务

Gau*_*shi 32 php logging laravel laravel-5 laravel-5.2

我们是否可以在laravel 5.2中为不同目的创建自定义日志文件,例如订单相关的日志条目应该在order.log中,对于与付款相关的内容,条目应该记录在payments.log中

我想找到最好的Laravel方式.

目前我们只能更改日志文件的频率(如每日,单一)或者我们可以更改默认日志文件的名称,即laravel.log

小智 42

有一个简单的方法:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

$log = ['orderId' => 10,
        'description' => 'Some description'];

//first parameter passed to Monolog\Logger sets the logging channel name
$orderLog = new Logger('order');
$orderLog->pushHandler(new StreamHandler(storage_path('logs/order.log')), Logger::INFO);
$orderLog->info('OrderLog', $log);
Run Code Online (Sandbox Code Playgroud)

在logs/order.log中输出:

[2017-04-30 00:00:00] order.INFO: OrderLog {"orderId":10, "description":"Some description"} []
Run Code Online (Sandbox Code Playgroud)

  • 这是添加额外日志记录的一种非常简洁的方法,只是一个问题是这会不断地将条目添加到单个日志文件中,还是会像laravel的日志记录一样处理日常模式,如果将其设置为每日日志,它将记录每个日志模式它自己的文件? (3认同)

小智 35

在这里你去...我花了很多时间为Monolog添加自定义功能,能够以适当的方式做到这一点.我尝试了许多不同的方式,但都有点hacky.最后,我找到了一种让这项功能正常工作的好方法....

由于应用程序很大,我需要单独的日志文件,并尽可能地维护现有的Laravel的Log界面.我需要这样的东西:

Log::write('audit', 'User logged in to the app.');
Log::info('event', 'User sent out 2 emails.');

解决方案:

App\Providers\AppServiceProvider.php(添加到注册函数)

//Facade to Object binding
$this->app->bind('chanellog', 'App\Helpers\ChannelWriter');
Run Code Online (Sandbox Code Playgroud)

config\app.php(添加到别名)

//Custom Alias Class
'ChannelLog' => App\Contracts\Facades\ChannelLog::class,
Run Code Online (Sandbox Code Playgroud)

应用程序\合同\外立面\ ChannelLog.php

<?php

namespace App\Contracts\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @see \Illuminate\Log\Writer
 */
class ChannelLog extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'chanellog';
    }
}
Run Code Online (Sandbox Code Playgroud)

应用程序\助手\ ChannelWriter.php

<?php

namespace App\Helpers;

use Monolog\Logger;

use App\Helpers\ChannelStreamHandler;

class ChannelWriter
{
    /**
     * The Log channels.
     *
     * @var array
     */
    protected $channels = [
        'event' => [ 
            'path' => 'logs/audit.log', 
            'level' => Logger::INFO 
        ],
        'audit' => [ 
            'path' => 'logs/audit.log', 
            'level' => Logger::INFO 
        ]
    ];

    /**
     * The Log levels.
     *
     * @var array
     */
    protected $levels = [
        'debug'     => Logger::DEBUG,
        'info'      => Logger::INFO,
        'notice'    => Logger::NOTICE,
        'warning'   => Logger::WARNING,
        'error'     => Logger::ERROR,
        'critical'  => Logger::CRITICAL,
        'alert'     => Logger::ALERT,
        'emergency' => Logger::EMERGENCY,
    ];

    public function __construct() {}

    /**
     * Write to log based on the given channel and log level set
     * 
     * @param type $channel
     * @param type $message
     * @param array $context
     * @throws InvalidArgumentException
     */
    public function writeLog($channel, $level, $message, array $context = [])
    {
        //check channel exist
        if( !in_array($channel, array_keys($this->channels)) ){
            throw new InvalidArgumentException('Invalid channel used.');
        }

        //lazy load logger
        if( !isset($this->channels[$channel]['_instance']) ){
            //create instance
            $this->channels[$channel]['_instance'] = new Logger($channel);
            //add custom handler
            $this->channels[$channel]['_instance']->pushHandler( 
                new ChannelStreamHandler( 
                    $channel, 
                    storage_path() .'/'. $this->channels[$channel]['path'], 
                    $this->channels[$channel]['level']
                )
            );
        }

        //write out record
        $this->channels[$channel]['_instance']->{$level}($message, $context);
    }

    public function write($channel, $message, array $context = []){
        //get method name for the associated level
        $level = array_flip( $this->levels )[$this->channels[$channel]['level']];
        //write to log
        $this->writeLog($channel, $level, $message, $context);
    }

    //alert('event','Message');
    function __call($func, $params){
        if(in_array($func, array_keys($this->levels))){
            return $this->writeLog($params[0], $func, $params[1]);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

应用程序\助手\ ChannelStreamHandler.php

<?php

namespace App\Helpers;

use Monolog\Handler\StreamHandler;

/**
 * Use channels to log into separate files
 *
 * @author Peter Feher
 */
class ChannelStreamHandler extends StreamHandler
{
    /**
     * Channel name
     * 
     * @var String 
     */
    protected $channel;

    /**
     * @param String $channel Channel name to write
     * @see parent __construct for params
     */
    public function __construct($channel, $stream, $level = Logger::DEBUG, $bubble = true, $filePermission = null, $useLocking = false)
    {
        $this->channel = $channel;

        parent::__construct($stream, $level, $bubble);
    }

    /**
     * When to handle the log record. 
     * 
     * @param array $record
     * @return type
     */
    public function isHandling(array $record)
    {
        //Handle if Level high enough to be handled (default mechanism) 
        //AND CHANNELS MATCHING!
        if( isset($record['channel']) ){
            return ( 
                $record['level'] >= $this->level && 
                $record['channel'] == $this->channel 
            );
        } else {
            return ( 
                $record['level'] >= $this->level
            );
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

在此之后,您可以在任何文件中执行:

use ChannelLog as Log;
...
function myFunction(){
    //Recommended (writes INFO to logs/event.log)
    Log::write('event', 'User sent out 3 voucher.')
    //Possible to use (writes ALERT to logs/audit.log)
    Log::alert('audit', 'User modified xyz entry.')
    //Or even: 
    Log::write('audit', 'User modified xyz entry.', ['user'=>1])
}
Run Code Online (Sandbox Code Playgroud)

  • 此外,通过将这些行添加到__construct方法,可以拥有每日日志文件:$ this-> channels ['event'] ['path'] ='logs/audit-'.日期('Ym-d').'的.log'; $ this-> channels ['audit'] ['path'] ='logs/audit-'.日期('Ym-d').'的.log'; (2认同)

Nir*_*hah 11

您可以尝试重新利用日志功能,将不同类型的日志写入不同的文件.这可以通过编辑bootstrap/app.php文件来完成:

$app->configureMonologUsing(function($monolog) {
    $bubble = false;
    $infoStreamHandler = new Monolog\Handler\StreamHandler( storage_path("/logs/orders.log"), Monolog\Logger::INFO, $bubble);
    $monolog->pushHandler($infoStreamHandler);

    $warningStreamHandler = new Monolog\Handler\StreamHandler( storage_path("/logs/logins.log"), Monolog\Logger::WARNING, $bubble);
    $monolog->pushHandler($warningStreamHandler);
});
Run Code Online (Sandbox Code Playgroud)

然后在您的代码中,您可以:

Log::info('Order was created', ['ORDER-123']);

Log::warning('User login', ['USER-1']);
Run Code Online (Sandbox Code Playgroud)

您可以使用此方法编辑所有可用的日志功能:

  • DEBUG
  • 信息
  • 注意
  • 警告
  • 错误
  • 危急
  • 警报
  • 紧急


fay*_*ayz 10

现在以更简单的方式支持此功能

  1. 建立频道

    转到:root/config/logging.php,下channels数组添加您的自定义频道,即

    '付款'=> [
          '驱动程序'=>'单个',
          '路径'=> storage_path('logs / payments.log'),
          'level'=>'info',
    ],
  2. 在您的路线或控制器中写入此日志

        Log::channel('payments')->info('A transaction has been made!');
    
    Run Code Online (Sandbox Code Playgroud)
  3. 可以在以下位置找到付款日志 /storage/logs/payments.log

注意:可扩展以进一步满足您的要求

Laravel 5.6版文档

  • 这会导致错误:`laravel.EMERGENCY:无法创建配置的记录器。使用紧急记录仪。` (2认同)

wir*_*d00 7

扩展ShQ的答案:

我注意到的一个问题是日志将被附加[] [],这是空的数组值$context$extra内部LineFormatter.format();

vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php

有两种方法可以解决这个问题,或者提供一种不包含构造函数的额外或上下文的格式LineFormatter,或者提供第四个参数$ignoreEmptyContextAndExtra= true.

ShQ答案中的所有文件保持不变,但ChannelStreamHandler必须更改.

ChannelStreamHandler:

<?php

namespace App\Helpers;

use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;

/**
 * Use channels to log into separate files
 *
 */
class ChannelStreamHandler extends StreamHandler
{
    /**
     * Channel name
     *
     * @var String
     */
    protected $channel;

    /**
     * @param String $channel Channel name to write
     * @param bool|int $stream
     * @param bool|int $level
     * @param bool $bubble
     * @param null $filePermission
     * @param bool $useLocking
     * @see parent __construct for params
     */
    public function __construct(
        $channel,
        $stream,
        $level = Logger::DEBUG,
        $bubble = true,
        $filePermission = null,
        $useLocking = false
    ) {
        $this->channel = $channel;

        $formatter = new LineFormatter(null, null, false, true);
        $this->setFormatter($formatter);

        parent::__construct($stream, $level, $bubble);
    }

    /**
     * When to handle the log record.
     *
     * @param array $record
     * @return bool
     */
    public function isHandling(array $record)
    {
        //Handle if Level high enough to be handled (default mechanism)
        //AND CHANNELS MATCHING!
        if (isset($record['channel'])) {
            return ($record['level'] >= $this->level && $record['channel'] == $this->channel);
        } else {
            return ($record['level'] >= $this->level);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

重要的变化是提供第4个真实的参数,即$ignoreEmptyContextAndExtra.这个参数告诉LineFormatter忽略任何context一个extra数组,如果为空:

$formatter = new LineFormatter(null, null, false, true);
$this->setFormatter($formatter);
Run Code Online (Sandbox Code Playgroud)

您必须确保运行monolog 1.22,因为它包含有关的错误修复ignoreEmptyContextAndExtra.

我还为ChannelWritter该类添加了info()的覆盖:

public function info($channel, $message, array $context = [])
{
    $level = array_flip($this->levels)[$this->channels[$channel]['level']];
    $this->writeLog($channel, $level, $message, $context);
}
Run Code Online (Sandbox Code Playgroud)

此外,我对ShQ解决方案中的"延迟加载记录器"不满意,因此修改为使用服务提供商/ IoC

替换ChannelWriter.writeLog():

public function writeLog(string $channel, string $level, string $message, array $context = [])
{
    if (!in_array($channel, array_keys($this->channels))) {
        throw new InvalidArgumentException('Invalid channel used.');
    }

    $logger = \App::make("{$channel}log");
    $channelHandler = new ChannelStreamHandler(
        $channel,
        storage_path() . '/' . $this->channels[$channel]['path'],
        $this->channels[$channel]['level']
    );
    $logger->pushHandler($channelHandler);
    $logger->{$level}($message);
}
Run Code Online (Sandbox Code Playgroud)

在你的AppServiceProvider:

    $this->app->bind('eventlog', function () {
        return new Logger('event');
    });

    $this->app->bind('auditlog', function () {
        return new Logger('audit');
    });
Run Code Online (Sandbox Code Playgroud)

我会尝试将它们捆绑在一起.

  • 您是否将此捆绑成一个包,可以共享?还可以创建每日日志文件吗? (2认同)