【问题标题】:laravel 5.2 custom log file for different taskslaravel 5.2 不同任务的自定义日志文件
【发布时间】:2016-10-15 01:35:52
【问题描述】:

我们可以在 laravel 5.2 中为不同目的创建自定义日志文件吗 就像订单相关的日志条目应该在 order.log 中,对于支付相关的东西,条目应该登录到 payment.log 中

我想找到最好的 Laravel 方式。

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

【问题讨论】:

标签: php laravel laravel-5 logging laravel-5.2


【解决方案1】:

您可以尝试重新调整日志函数的用途,将不同类型的日志写入不同的文件。这可以通过编辑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);
});

然后在你的代码中,你可以这样做:

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

Log::warning('User login', ['USER-1']);

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

  • 调试
  • 信息
  • 通知
  • 警告
  • 错误
  • 严重
  • 提醒
  • 紧急情况

【讨论】:

  • 非常适合按错误级别单独记录日志,谢谢。
  • 不使用这个storage_path("/logs/orders.log"),我们可以使用env()在外面设置日志路径
  • 是的,您可以将storage_path("/logs/orders.log") 行更改为env('LOG_PATH', storage_path("/logs/orders.log")),这样如果没有定义环境,日志可以回退到 storage_path。
  • 我已经赞成这个答案,但如果它链接到文档会更有帮助:laravel.com/docs/5.2/errors#configuration
【解决方案2】:

给你...我花了很多时间向 Monolog 添加自定义功能,它能够以适当的方式做到这一点。我尝试了很多不同的方法,但都有点老套。最后我找到了一个让这个功能正常工作的好方法......

由于应用程序很大,我需要单独的日志文件,并尽可能维护现有的 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');

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

//Custom Alias Class
'ChannelLog' => App\Contracts\Facades\ChannelLog::class,

App\Contracts\Facades\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';
    }
}

App\Helpers\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]);
        }
    }

}

App\Helpers\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
            );
        }
    }

}

之后,你可以在任何文件中做:

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])
}

【讨论】:

  • 另外,通过将这些行添加到 __construct 方法可以拥有每日日志文件: $this->channels['event']['path'] = 'logs/audit-' 。日期('Y-m-d')。 '。日志'; $this->channels['audit']['path'] = 'logs/audit-' 。日期('Y-m-d')。 '.log';
  • 这段代码在日志消息中创建了额外的 [] [],知道吗?
  • @codebob 请参阅下面关于[] [] 问题的回答。这是因为它使用带有默认格式化程序的默认 LineFormatter.php 实例
  • 我也有同样的[][]问题,怎么去掉?
  • 只是一个注释。它不会删除 log_max_files 中指定的旧文件。
【解决方案3】:

对于我在 Laravel 5.3 中,我不确定它是否是我之前安装的,但我发现 bootstrap/app.php 对我不起作用。

我需要把它放在 app/Providers/AppServiceProvider.php 中。

n.b.这是我之前从 config 设置日志级别的地方,所以我最终得到了 3 个日志处理程序。

public function register()
{
   $monolog = Log::getMonolog();
   foreach ($monolog->getHandlers() as $handler) {
      $handler->setLevel(Config::get('app.log_level'));
   }

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

   $warningStreamHandler = new \Monolog\Handler\StreamHandler( storage_path("logs/warning.log"), \Monolog\Logger::WARNING, $bubble);
   $monolog->pushHandler($warningStreamHandler);

}

【讨论】:

【解决方案4】:

扩展 ShQ 的回答:

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

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);
        }
    }

}

重要的变化是提供了第四个 true 参数,即$ignoreEmptyContextAndExtra。此参数告诉LineFormatter 如果为空,则忽略contextextra 数组:

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

您还必须确保您正在运行独白 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);
}

此外,我对 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);
}

在你的AppServiceProvider:

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

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

我会试着把它打包成一个包。

【讨论】:

  • 你有没有把它打包成一个可以分享的包?这也可以创建每日日志文件吗?
【解决方案5】:

有一个简单的方法:

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);

logs/order.log 中的输出:

[2017-04-30 00:00:00] order.INFO: OrderLog {"orderId":10, "description":"Some description"} []

【讨论】:

  • 这是一种添加额外日志记录的非常简洁和简单的方法,只是一个问题会继续向单个日志文件添加条目,还是它也适用于 laravel 的日志记录等日常模式,如果你将它设置为每天它会在它自己的文件中记录每一天?
  • @massreuy Logger() 将字符串作为类构造函数参数(至少在 laravel 5.5.28 中)。所以对于 5.5.28,正确的方法是 Logger('order') (我刚刚尝试过干净的 laravel 5.5.28 项目)。我不确切知道您是否犯了错误,所以我不编辑您的答案,而是写评论。我什至无法编辑,因为编辑可能至少修改了 6 个字符)
【解决方案6】:

基于 ShQ 的答案,一个更短更简单的记录器助手,允许您即时记录到自定义文件。您还可以添加自定义处理程序并设置文件路径。

应用\助手

<?php
/**
 * Logger helper to log into different files
 *
 * @package    App\Helpers
 * @author     Romain Laneuville <romain.laneuville@hotmail.fr>
 */

namespace App\Helpers;

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

/**
 * Class LogToChannels
 *
 * @package App\Helpers
 */
class LogToChannels
{
    /**
     * The LogToChannels channels.
     *
     * @var Logger[]
     */
    protected $channels = [];

    /**
     * LogToChannels constructor.
     */
    public function __construct()
    {
    }

    /**
     * @param string $channel The channel to log the record in
     * @param int    $level   The error level
     * @param string $message The error message
     * @param array  $context Optional context arguments
     *
     * @return bool Whether the record has been processed
     */
    public function log(string $channel, int $level, string $message, array $context = []): bool
    {
        // Add the logger if it doesn't exist
        if (!isset($this->channels[$channel])) {
            $handler = new StreamHandler(
                storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channel . '.log'
            );

            $this->addChannel($channel, $handler);
        }

        // LogToChannels the record
        return $this->channels[$channel]->{Logger::getLevelName($level)}($message, $context);
    }

    /**
     * Add a channel to log in
     *
     * @param string           $channelName The channel name
     * @param HandlerInterface $handler     The channel handler
     * @param string|null      $path        The path of the channel file, DEFAULT storage_path()/logs
     *
     * @throws \Exception When the channel already exists
     */
    public function addChannel(string $channelName, HandlerInterface $handler, string $path = null)
    {
        if (isset($this->channels[$channelName])) {
            throw new \Exception('This channel already exists');
        }

        $this->channels[$channelName] = new Logger($channelName);
        $this->channels[$channelName]->pushHandler(
            new $handler(
                $path === null ?
                    storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channelName . '.log' :
                    $path . DIRECTORY_SEPARATOR . $channelName . '.log'
            )
        );
    }

    /**
     * Adds a log record at the DEBUG level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function debug(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::DEBUG, $message, $context);
    }

    /**
     * Adds a log record at the INFO level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function info(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::INFO, $message, $context);
    }

    /**
     * Adds a log record at the NOTICE level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function notice(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::NOTICE, $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function warn(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::WARNING, $message, $context);
    }

    /**
     * Adds a log record at the WARNING level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function warning(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::WARNING, $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function err(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::ERROR, $message, $context);
    }

    /**
     * Adds a log record at the ERROR level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function error(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::ERROR, $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function crit(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::CRITICAL, $message, $context);
    }

    /**
     * Adds a log record at the CRITICAL level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return Boolean Whether the record has been processed
     */
    public function critical(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::CRITICAL, $message, $context);
    }

    /**
     * Adds a log record at the ALERT level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function alert(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::ALERT, $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function emerg(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::EMERGENCY, $message, $context);
    }

    /**
     * Adds a log record at the EMERGENCY level.
     *
     * @param  string $channel The channel name
     * @param  string $message The log message
     * @param  array  $context The log context
     *
     * @return bool Whether the record has been processed
     */
    public function emergency(string $channel, string $message, array $context = []): bool
    {
        return $this->log($channel, Logger::EMERGENCY, $message, $context);
    }
}

App\Providers\AppServiceProvider.php(添加注册功能)

//Facade to Object binding
$this->app->bind('LogToChannels', 'App\Helpers\LogToChannels');

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

// Custom Alias Class
'Log' => App\Contracts\Facades\LogToChannels::class

然后您可以在应用中的任何位置调用

Log::info('logger_name', 'Log message');
Log::error('other_logger_name', 'Log message', $someContext);

您甚至可以通过调用自定义记录器输出

Log::addChannel('channel_name', $customHandler);

当您在应用中的任何位置调用它的名称时,它就会被访问。

【讨论】:

  • 简单地使用Log::info('log message', $destinationPath);会很棒
  • DRY 使用 warn、emerg、crit 和 err 方法。
【解决方案7】:

将日志输出到不同文件的最快方法

Log::useFiles('path/to/file.log');
Log::info('Info');

【讨论】:

    【解决方案8】:

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

    1. 创建频道

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

          'payments' => [
              'driver' => 'single',
              'path' => storage_path('logs/payments.log'),
              'level' => 'info',
        ],
    
    1. 在您的路由或控制器中写入此日志
        Log::channel('payments')->info('A transaction has been made!');
    
    1. 支付日志可以在/storage/logs/payments.log找到

    注意:可扩展以进一步提高您的要求

    Laravel 5.6 版Docs

    【讨论】:

    • 这个。是的。谢谢你,先生。这应该成为公认的答案。
    • 顺便说一下,它是 /config/logging.php 而不是 /app/config/logging.php
    • 是的,这可能会造成混淆,这里的 app 是指根文件夹。我会进行编辑。
    • 这会导致错误:laravel.EMERGENCY: Unable to create configured logger. Using emergency logger.
    【解决方案9】:
    Solution:
    
    step1: create a channel inside config/logging.php file
    
    example :
    
    'channels' => [
        'single' => [
        'driver' => 'single', 
        'path' => storage_path('logs/laravel.log'),
        'level' => 'debug',
    ],
    
    'web' => [
          'driver' => 'single',
          'path' => storage_path('logs/web/web.log'),
       ],
    
    ]
    
    Step2: Now set dynamic path from the controller  like this
    
    config(['logging.channels.web.path' => storage_path('logs/web/'.time().'.log')]);
    
    Step3 : now generate your log
    
      Log::channel('web')->info("your message goes here");
    
    Enjoy :)
    

    【讨论】:

      【解决方案10】:

      我管理了我自己的日志函数,它可以放在 app 目录中的 helper.php 文件中。

      if ( ! function_exists( 'write_log' ) ) {
          /**
           * Write log to log file
           *
           * @param string|array|object $log
           */
          function write_log( $log ) {
              if ( env('APP_LOG_LEVEL', 'debug') == 'debug' ) {
                  if ( is_array( $log ) || is_object( $log ) ) {
                      file_put_contents(laravelInstallDir().'../debug.log', print_r( $log, true ), FILE_APPEND);
                  } else {
                      file_put_contents(laravelInstallDir().'../debug.log', $log, FILE_APPEND);
                  }
              }
          }
      }
      

      请根据需要调整路径 laravelInstallDir().'../debug.log'

      【讨论】:

        猜你喜欢
        • 2017-09-01
        • 2020-02-04
        • 2019-08-03
        • 1970-01-01
        • 2014-08-13
        • 2016-04-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多