Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to create custom monolog log channel Laravel 5.6 #25170

Closed
Tarasovych opened this issue Aug 9, 2018 · 19 comments
Closed

Trying to create custom monolog log channel Laravel 5.6 #25170

Tarasovych opened this issue Aug 9, 2018 · 19 comments

Comments

@Tarasovych
Copy link

Tarasovych commented Aug 9, 2018

  • Laravel Version: 5.6.*
  • PHP Version: 7.1
  • Database Driver & Version: MongoDB 3.2

Description:

My 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'
                ]
            ]
        ],

My .env: LOG_CHANNEL=stack.

I'm sure that MongoDB database logs exists, test collection too.

I'm trying to execute php artisan config:cache, I get an error:

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

Full trace:
Expected $document to have type "array or object" but found "string" {"exception":"[object] (MongoDB\Exception\InvalidArgumentException(code: 0): Expected $document to have type "array or object" but found "string" at path_to_my_project\vendor\mongodb\mongodb\src\Exception\InvalidArgumentException.php:32)
[stacktrace]
#0 path_to_my_project\vendor\mongodb\mongodb\src\Operation\InsertOne.php(70): MongoDB\Exception\InvalidArgumentException::invalidType('$document', '[2018-08-09 22:...', 'array or object')
#1 path_to_my_project\vendor\mongodb\mongodb\src\Collection.php(889): MongoDB\Operation\InsertOne->__construct('logs', 'prod', '[2018-08-09 22:...', Array)
#2 path_to_my_project\vendor\monolog\monolog\src\Monolog\Handler\MongoDBHandler.php(46): MongoDB\Collection->insertOne('[2018-08-09 22:...')
#3 path_to_my_project\vendor\monolog\monolog\src\Monolog\Handler\AbstractProcessingHandler.php(37): Monolog\Handler\MongoDBHandler->write(Array)
#4 path_to_my_project\vendor\monolog\monolog\src\Monolog\Logger.php(337): Monolog\Handler\AbstractProcessingHandler->handle(Array)
#5 path_to_my_project\vendor\monolog\monolog\src\Monolog\Logger.php(616): Monolog\Logger->addRecord(400, 'Your configurat...', Array)
#6 path_to_my_project\vendor\laravel\framework\src\Illuminate\Log\Logger.php(176): Monolog\Logger->error('Your configurat...', Array)
#7 path_to_my_project\vendor\laravel\framework\src\Illuminate\Log\Logger.php(87): Illuminate\Log\Logger->writeLog('error', 'Your configurat...', Array)
#8 path_to_my_project\vendor\laravel\framework\src\Illuminate\Log\LogManager.php(526): Illuminate\Log\Logger->error('Your configurat...', Array)
#9 path_to_my_project\vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php(113): Illuminate\Log\LogManager->error('Your configurat...', Array)
#10 path_to_my_project\app\Exceptions\Handler.php(41): Illuminate\Foundation\Exceptions\Handler->report(Object(LogicException))
#11 path_to_my_project\vendor
unomaduro\collision\src\Adapters\Laravel\ExceptionHandler.php(60): App\Exceptions\Handler->report(Object(LogicException))
#12 path_to_my_project\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(353): NunoMaduro\Collision\Adapters\Laravel\ExceptionHandler->report(Object(LogicException))
#13 path_to_my_project\vendor\laravel\framework\src\Illuminate\Foundation\Console\Kernel.php(124): Illuminate\Foundation\Console\Kernel->reportException(Object(LogicException))
#14 path_to_my_project\artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#15 {main}
"}

What's wrong? I guess, my config was amde accordingly to documentation.

What I've tried so far:

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

same error.

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

LogicException : Your configuration files are not serializable.

@Tarasovych Tarasovych changed the title Trying to create custom log channel Laravel 5.6 Trying to create custom monolog log channel Laravel 5.6 Aug 9, 2018
@mfn
Copy link
Contributor

mfn commented Aug 9, 2018

#2 path_to_my_project\vendor\monolog\monolog\src\Monolog\Handler\MongoDBHandler.php(46): MongoDB\Collection->insertOne('[2018-08-09 22:...')

I believe here we see the problem: a string is passed which looks like from the default line formatter, but insertOne expects either an array or an string.

I don't see a formatter in your configuration, please see if this works:

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

@Tarasovych
Copy link
Author

@mfn thank you! Didn't know that formatter is required. I'm done with clearing config cache.

But now I'm trying to log something and I get

Class 'MongoDate' not found in .../vendor/monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php on line 103

@Tarasovych
Copy link
Author

phpinfo():

MongoDB support => enabled
MongoDB extension version => 1.5.0
MongoDB extension stability => stable
libbson bundled version => 1.11.0
libmongoc bundled version => 1.11.0
libmongoc SSL => enabled
libmongoc SSL library => OpenSSL
libmongoc crypto => enabled
libmongoc crypto library => libcrypto
libmongoc crypto system profile => disabled
libmongoc SASL => enabled
libmongoc ICU => disabled
libmongoc compression => disabled

@mfn
Copy link
Contributor

mfn commented Aug 9, 2018

It seems that \MongoDate is something outdated/older, define by the "PECL mongo" extension, see the warning on http://php.net/manual/en/class.mongodate.php

This extension that defines this class is deprecated. Instead, the MongoDB extension should be used. Alternatives to this class include:
MongoDB\BSON\UTCDateTime

The referenced class OTOH MongoDB\BSON\UTCDateTime is from the "mongodb" extension you're using => http://php.net/manual/en/class.mongodb-bson-utcdatetime.php

It seems that \Monolog\Handler\MongoDBHandler::__construct is prepared to handle different mongodb clients, but \Monolog\Formatter\MongoDBFormatter only works with "PECL mongo".

For you to move forward, my advice is to simply copy that MongoDBFormatter into your application and adapt it.

I found your Monolog issue that you created about this problem, so maybe you can figure out what's with the formatter and maybe contribute back a version working with both clients.

@mfn
Copy link
Contributor

mfn commented Aug 9, 2018

Oh, I just found out: Monologs master has an updated version: https://github.com/Seldaek/monolog/blob/master/src/Monolog/Formatter/MongoDBFormatter.php

But it's a different version in the latest release (1.23.0).

Monologs last release was >1 year ago and a lot has happened since then. I guess the 1.x version is maintenance only (it contains the version you're using, see https://github.com/Seldaek/monolog/blob/1.x/src/Monolog/Formatter/MongoDBFormatter.php ) and at somepoint there will probably a 2.x release with the version you need.

In this case: see if you can directly copy over that version into your project and work with that.

@Tarasovych
Copy link
Author

Tarasovych commented Aug 9, 2018

@mfn thank you a lot!
So I

And it worked!

@Tarasovych
Copy link
Author

Maybe it's reasonable to have newer version inside https://github.com/laravel/framework/blob/5.6/composer.json#L25

@mfn
Copy link
Contributor

mfn commented Aug 10, 2018

@Tarasovych there is no newer release of Monolog with what you need, so there's nothing to do for Laravel.

Please close the issue.

@Tarasovych
Copy link
Author

Tarasovych commented Aug 10, 2018

I got one more error connected with this. So my logging.php:

        'mongo' => [
            'driver' => 'monolog',
            'handler' => \Monolog\Handler\MongoDBHandler::class,
            'handler_with' => [
                'mongo' => new MongoDB\Client(), // MongoDB\Client()::class doesn't work
                'database' => 'logs',
                'collection' => 'prod'
            ],
            'formatter' => App\MongoDBFormatter::class
        ]

I can't execute php artisan config:cache. It returns

LogicException : Your configuration files are not serializable.

Exception trace:

  1   Error::("Call to undefined method MongoDB\Driver\Manager::__set_state()")
      ...\bootstrap\cache\config.php:445

  2   require()
      ...\vendor\laravel\framework\src\Illuminate\Foundation\Console\ConfigCacheCommand.php:64

I can run php artisan config:clear, and I can log in MongoDB by the way.

P. S. I've also tried

                'mongo' => [
                    'manager' => MongoDB\Driver\Manager::class,
                    'uri' => 'mongodb://localhost:27017',
                    'typeMap' => [
                        'array' => MongoDB\Model\BSONArray::class,
                        'document' => MongoDB\Model\BSONDocument::class,
                        'root' => MongoDB\Model\BSONDocument::class
                    ],
                    'writeConcern' => MongoDB\Driver\WriteConcern::class
                ],

without any success.

I understand why I got "not serializable", but that config works... What to do now?

@mfn
Copy link
Contributor

mfn commented Aug 11, 2018

Instead of

            'mongo' => new MongoDB\Client(), // MongoDB\Client()::class doesn't work

did you try

            'mongo' => new MongoDB\Client::class,

(no parenthesis)

@Tarasovych
Copy link
Author

@mfn IDE doesn't allow me to do that)

syntax error, unexpected 'class' (T_CLASS), expecting variable (T_VARIABLE) or '$'

@mfn
Copy link
Contributor

mfn commented Aug 11, 2018

Sorry, typo:

 'mongo' => MongoDB\Client::class,

(no new)

@Tarasovych
Copy link
Author

Tarasovych commented Aug 11, 2018

@mfn I tried that, doesn't work.

P. S. I meant, there is no problem with config serialization, but nothing logs into mongo.

@Tarasovych
Copy link
Author

I've also tried to call different methods from MongoDB\Client. Like 'mongo' => (string)(new MongoDB\Client()->...) in hope it will work, but no success. If I know what actually mongo might be for success logging - connection URL or something other.

@mfn
Copy link
Contributor

mfn commented Aug 11, 2018

I think I now understand the issue better.

You're creating driver=monolog which calls \Illuminate\Log\LogManager::createMonologDriver and creates a MongoDBHandler instance and and passes handler_with as its arguments.

The problem is: the arguments are passed literally, that's why the first argument, which is only class string reference to MongoDB\Client, is passed literally to the MongoDBHandler => there's nothing "in between" which understands this needs to be an actual instance of MongoDB\Client (that's why in your examples it works with new in the config but has the other issue with caching the config).

I think what you want to achieve is not possible directly with the driver=monolog adapter ATM because of how MongoDBHandler expects its argument.

What you need is more flexibility and therefore I suggest you create a custom driver like this:

            'mongo' => [
                'driver' => 'custom',
                'via' => YourOwnMongodbAdapter::class,
                'name' => 'default',
                'database' => 'logs',
                'collection' => 'test',
            ],

(see also https://laravel.com/docs/5.6/logging#creating-channels-via-factories )

Then YourOwnMongodbAdapter::__invoke receives your $config array and you can create the Monolog driver the way you need it; untested but it should something similar to this:

<?php

use Monolog\Handler\MongoDBHandler;

class YourOwnMongodbAdapter
{
    public function __invoke(array $config)
    {
        $handler = new MongoDBHandler(
            new MongoDB\Client(),
            $config['database'],
            $config['collection']
        );

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

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

@Tarasovych
Copy link
Author

Tarasovych commented Aug 11, 2018

@mfn thanks! Now I can execute php artisan config:cache without errors, but when logging, I got

Your configuration files are not serializable.

yet in Mongo.
Update: ^ that was my miss, because of date format issue. So, it works!

Also I get 1970 year using UTCDateTime class for formatting data, but this is another question...

@Tarasovych
Copy link
Author

So I went from

    protected function formatDate(\DateTime $value, $nestingLevel)
    {
        return new UTCDateTime($value->getTimestamp());
    }

to

    protected function formatDate(\DateTime $value, $nestingLevel)
    {
        return $value;
    }

and I have correct date.

@Amirhb
Copy link

Amirhb commented Sep 24, 2018

you can also try this : https://github.com/Amirhb/laravel-mongodb-log
tell me if you needed any update. doc: https://packagist.org/packages/amirhb/laravel-mongodb-log

@ghost
Copy link

ghost commented Feb 5, 2023

You can register the mongo client in service provider and live the $mongodb value empty:

Laravel 9.x

// AppServiceProvider.php

// register function
$this->app->when(\Monolog\Handler\MongoDBHandler::class)
    ->needs('$mongodb')
    ->give(app(\MongoDB\Client::class));
// logging.php
'mongo' => [
    'driver' => 'monolog',
    'handler' => \Monolog\Handler\MongoDBHandler::class,
    'formatter' =>\Monolog\Formatter\MongoDBFormatter::class,
    'with' => [
        'database' => 'logs',
        // 'mongo' => new MongoDB\Client(), <-- Remove this key
        'collection' => 'prod',
    ],
]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants