正在尝试创建自定义日志通道 Laravel 5.6
Trying to create custom log channel Laravel 5.6
里面 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
数组中传递 class 或 class 实例的字样。
这个配置
'mongo' => [
'driver' => 'monolog',
'handler' => \Monolog\Handler\MongoDBHandler::class,
]
还有 returns 错误,但我预计 InvalidArgumentException。
这个
'mongo' => [
'driver' => 'monolog',
'handler' => new \Monolog\Handler\MongoDBHandler(new MongoDB\Client(),'logs', 'prod'),
]
导致 LogicException : Your configuration files are not serializable.
.
嗯,显然您的 'logger' 想要一个数据数组。我猜它可能是 JSON 格式,所以你可以在将它输入到你的记录器之前尝试 json_decode 它。最好先登录到其他地方,以便首先实际查看 'pushed' 数据(一个简单的 var 转储就可以)。
此外,我还没有足够的声誉直接回复所以请不要评论这应该是直接回复 'reply';
解决方案mfn:
将 formatter
添加到通道数组:
'formatter' => \Monolog\Formatter\MongoDBFormatter::class,
还创建自定义格式化程序 class,因为当前 Laravel 版本中使用的 mongo formatter 已过时。
<?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
class:
<?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
class(稍作修改,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;
}
}
里面 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
数组中传递 class 或 class 实例的字样。
这个配置
'mongo' => [
'driver' => 'monolog',
'handler' => \Monolog\Handler\MongoDBHandler::class,
]
还有 returns 错误,但我预计 InvalidArgumentException。
这个
'mongo' => [
'driver' => 'monolog',
'handler' => new \Monolog\Handler\MongoDBHandler(new MongoDB\Client(),'logs', 'prod'),
]
导致 LogicException : Your configuration files are not serializable.
.
嗯,显然您的 'logger' 想要一个数据数组。我猜它可能是 JSON 格式,所以你可以在将它输入到你的记录器之前尝试 json_decode 它。最好先登录到其他地方,以便首先实际查看 'pushed' 数据(一个简单的 var 转储就可以)。
此外,我还没有足够的声誉直接回复所以请不要评论这应该是直接回复 'reply';
解决方案mfn:
将 formatter
添加到通道数组:
'formatter' => \Monolog\Formatter\MongoDBFormatter::class,
还创建自定义格式化程序 class,因为当前 Laravel 版本中使用的 mongo formatter 已过时。
<?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
class:
<?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
class(稍作修改,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;
}
}