Попытка создать пользовательский лог-канал Laravel 5.6
Внутри config/logging.php
:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'mongo'],
],
'mongo' => [
'driver' => 'monolog',
'handler' => MonologHandlerMongoDBHandler::class,
'handler_with' => [
'mongo' => new MongoDBClient(),
'database' => 'logs',
'collection' => 'test'
]
]
],
.env
: LOG_CHANNEL=stack
Я уверен, что база данных MongoDB logs
существует, коллекция test
тоже.
Я пытаюсь записать некоторые данные, находящиеся внутри php artisan tinker
:
Log::info('test');
Я получил исключение
Ожидалось, что $document будет иметь тип "массив или объект", но найдено "строка"
Даже я пытаюсь php artisan config:cache
я получил это исключение.
Что может быть не так?
Обновление:
'mongo' => MongoDBClient::class,
вызывает ту же ошибку. Между прочим, в документации нет ни слова о передаче класса или экземпляра класса внутри массива handler_with
.
Эта конфигурация
'mongo' => [
'driver' => 'monolog',
'handler' => MonologHandlerMongoDBHandler::class,
]
Также возвращает ошибку, но я ожидал InvalidArgumentException.
Этот
'mongo' => [
'driver' => 'monolog',
'handler' => new MonologHandlerMongoDBHandler(new MongoDBClient(),'logs', 'prod'),
]
Причины LogicException : Your configuration files are not serializable.
.
2 ответа:
Решение по НБН :
добавьтеformatter
в массив каналов:'formatter' => \Monolog\Formatter\MongoDBFormatter::class,
Также создайте пользовательский класс formatter, потому что 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
class (модифицированный немного, оригинальный с некоторыми устаревшими классами, по путь):<?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; } }
Ну, очевидно, что ваш "регистратор" хочет иметь массив данных. Я думаю, что это может быть формат JSON, так что вы можете попробовать json_decode его, прежде чем вводить его в свой регистратор. Было бы лучше сначала войти во что-то другое, чтобы действительно увидеть "выталкиваемые" данные (простой дамп var будет делать).
Кроме того, у меня еще недостаточно репутации, чтобы ответить прямо, поэтому, пожалуйста, не комментируйте, что это должен был быть прямой "ответ";