php - 尝试创建自定义日志 channel Laravel 5.6

标签 php laravel-5.6 monolog

config/logging.php 中:

'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['single', 'mongo'],
        ],

        'mongo' => [
            'driver' => 'monolog',
            'handler' => \Monolog\Handler\MongoDBHandler::class,
            'handler_with' => [
                'mongo' => new MongoDB\Client(),
                'database' => 'logs',
                'collection' => 'test'
            ]
        ]
    ],

.env:LOG_CHANNEL=stack

我确定 MongoDB 数据库 logs 存在,test 集合也存在。

我正在尝试记录 php artisan tinker 中的一些数据:

Log::info('test');

我遇到了异常

Expected $document to have type "array or object" but found "string"

即使我尝试 php artisan config:cache 我也遇到了这个异常。

有什么问题吗?


更新: 'mongo' => MongoDB\Client::class, 导致同样的错误。顺便说一句,文档中没有关于在 handler_with 数组中传递类或类实例的文字。

这个配置

'mongo' => [
            'driver' => 'monolog',
            'handler' => \Monolog\Handler\MongoDBHandler::class,
        ]

也返回错误,但我预计 InvalidArgumentException .

这个

'mongo' => [
    'driver' => 'monolog',
    'handler' => new \Monolog\Handler\MongoDBHandler(new MongoDB\Client(),'logs', 'prod'),
]

导致 LogicException:您的配置文件不可序列化。

最佳答案

解决方案 mfn :
formatter 添加到 channel 数组:

'formatter' => \Monolog\Formatter\MongoDBFormatter::class,

还创建自定义格式化程序类,因为 mongo formatter当前 Laravel 版本中使用的已过时。

<?php
/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace Monolog\Formatter;
/**
 * Formats a record for use with the MongoDBHandler.
 *
 * @author Florian Plattner <me@florianplattner.de>
 */
class MongoDBFormatter implements FormatterInterface
{
    private $exceptionTraceAsString;
    private $maxNestingLevel;
    /**
     * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
     * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
     */
    public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true)
    {
        $this->maxNestingLevel = max($maxNestingLevel, 0);
        $this->exceptionTraceAsString = (bool) $exceptionTraceAsString;
    }
    /**
     * {@inheritDoc}
     */
    public function format(array $record)
    {
        return $this->formatArray($record);
    }
    /**
     * {@inheritDoc}
     */
    public function formatBatch(array $records)
    {
        foreach ($records as $key => $record) {
            $records[$key] = $this->format($record);
        }
        return $records;
    }
    protected function formatArray(array $record, $nestingLevel = 0)
    {
        if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
            foreach ($record as $name => $value) {
                if ($value instanceof \DateTime) {
                    $record[$name] = $this->formatDate($value, $nestingLevel + 1);
                } elseif ($value instanceof \Exception) {
                    $record[$name] = $this->formatException($value, $nestingLevel + 1);
                } elseif (is_array($value)) {
                    $record[$name] = $this->formatArray($value, $nestingLevel + 1);
                } elseif (is_object($value)) {
                    $record[$name] = $this->formatObject($value, $nestingLevel + 1);
                }
            }
        } else {
            $record = '[...]';
        }
        return $record;
    }
    protected function formatObject($value, $nestingLevel)
    {
        $objectVars = get_object_vars($value);
        $objectVars['class'] = get_class($value);
        return $this->formatArray($objectVars, $nestingLevel);
    }
    protected function formatException(\Exception $exception, $nestingLevel)
    {
        $formattedException = array(
            'class' => get_class($exception),
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'file' => $exception->getFile() . ':' . $exception->getLine(),
        );
        if ($this->exceptionTraceAsString === true) {
            $formattedException['trace'] = $exception->getTraceAsString();
        } else {
            $formattedException['trace'] = $exception->getTrace();
        }
        return $this->formatArray($formattedException, $nestingLevel);
    }
    protected function formatDate(\DateTime $value, $nestingLevel)
    {
        return new UTCDateTime($value->getTimestamp());
    }
}

更新

到目前为止我自己的实现:

config/logging.php:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => [... , 'mongo'],
    ],

    ...

    'mongo' => [
        'driver' => 'custom',
        'handler' => \Monolog\Handler\MongoDBHandler::class,
        'via' => \App\Logging\MongoLogger::class, //place MongoLogger where you want
        'name' => 'mongo',
        'host' => env('MONGODB_HOST'), //you can keep these right here in cofig, but I prefer .env because its scaleable
        'port' => env('MONGODB_PORT'),
        'database' => env('MONGODB_DATABASE'),
        'collection' => env('MONGODB_COLLECTION')
    ]
],

MongoLogger 类:

<?php

namespace App\Logging; //once again, palce this where you want

use App\MongoDBFormatter;
use MongoDB\Client;
use Monolog\Handler\MongoDBHandler;
use Monolog\Logger;

class MongoLogger
{
    public function __invoke(array $config)
    {
        $handler = new MongoDBHandler(
            new Client('mongodb://'.$config['host'].':'.$config['port']),
            $config['database'],
            $config['collection']
        );

        $handler->setFormatter(new MongoDBFormatter());

        return new Logger($config['name'], [$handler]);
    }
}

MongoDBFormatter 类(稍作修改,original one with some deprecated classes, by the way):

<?php
/*
 * This file is part of the Monolog package.
 *
 * (c) Jordi Boggiano <j.boggiano@seld.be>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
namespace App; //once again, palce this where you want

use Monolog\Formatter\FormatterInterface;

/**
 * Formats a record for use with the MongoDBHandler.
 *
 * @author Florian Plattner <me@florianplattner.de>
 */
class MongoDBFormatter implements FormatterInterface
{
    private $exceptionTraceAsString;
    private $maxNestingLevel;
    /**
     * @param int  $maxNestingLevel        0 means infinite nesting, the $record itself is level 1, $record['context'] is 2
     * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings
     */
    public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = true)
    {
        $this->maxNestingLevel = max($maxNestingLevel, 0);
        $this->exceptionTraceAsString = (bool) $exceptionTraceAsString;
    }
    /**
     * {@inheritDoc}
     */
    public function format(array $record)
    {
        return $this->formatArray($record);
    }
    /**
     * {@inheritDoc}
     */
    public function formatBatch(array $records)
    {
        foreach ($records as $key => $record) {
            $records[$key] = $this->format($record);
        }
        return $records;
    }
    protected function formatArray(array $record, $nestingLevel = 0)
    {
        if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) {
            foreach ($record as $name => $value) {
                if ($value instanceof \DateTime) {
                    $record[$name] = $this->formatDate($value, $nestingLevel + 1);
                } elseif ($value instanceof \Exception) {
                    $record[$name] = $this->formatException($value, $nestingLevel + 1);
                } elseif (is_array($value)) {
                    $record[$name] = $this->formatArray($value, $nestingLevel + 1);
                } elseif (is_object($value)) {
                    $record[$name] = $this->formatObject($value, $nestingLevel + 1);
                }
            }
        } else {
            $record = '[...]';
        }
        return $record;
    }
    protected function formatObject($value, $nestingLevel)
    {
        $objectVars = get_object_vars($value);
        $objectVars['class'] = get_class($value);
        return $this->formatArray($objectVars, $nestingLevel);
    }
    protected function formatException(\Exception $exception, $nestingLevel)
    {
        $formattedException = array(
            'class' => get_class($exception),
            'message' => $exception->getMessage(),
            'code' => $exception->getCode(),
            'file' => $exception->getFile() . ':' . $exception->getLine(),
        );
        if ($this->exceptionTraceAsString === true) {
            $formattedException['trace'] = $exception->getTraceAsString();
        } else {
            $formattedException['trace'] = $exception->getTrace();
        }
        return $this->formatArray($formattedException, $nestingLevel);
    }
    protected function formatDate(\DateTime $value, $nestingLevel)
    {
        return $value;
    }
}

Answser source .

关于php - 尝试创建自定义日志 channel Laravel 5.6,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51716155/

相关文章:

javascript - Laravel/Croppie 2.6.2 : How to get output image in JPEG instead of default PNG?

php - 在 laravel 查询生成器中使用数字,其中语句返回正确的响应,但在使用包含数字的变量时返回错误的响应

php - 在 Symfony2 中使用 Monolog 旋转日志

php - 使用 Monolog 记录完整的堆栈跟踪

javascript - 使用 JavaScript 将 PHP 值传递到另一个页面时出错

php - 如何销毁 Codeigniter 库实例

php - 为什么 composer 没有正确安装 laravel 5.6?

symfony - 如何在 Symfony 中使用 Monolog 断言记录了一行

php - 在 laravel 中存储来自队列的已处理作业

php - preg_replace 所有空格