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

[4.x] Add model hydrations to model watcher #1000

Merged
merged 4 commits into from
Dec 13, 2020
Merged

[4.x] Add model hydrations to model watcher #1000

merged 4 commits into from
Dec 13, 2020

Conversation

paras-malhotra
Copy link
Contributor

@paras-malhotra paras-malhotra commented Dec 12, 2020

Motivation

Re-attempt of #999 without a separate tab or watcher.

This PR adds the ability to record model hydrations within the Model Watcher (without a separate tab or watcher). This would help to know how many models are being hydrated for a request - something that can have a significant on performance / memory usage. Inspired from a similar popular feature of Debugbar: barryvdh/laravel-debugbar#947.

There are no breaking changes with this PR. To enable recording of model hydrations, we would need to enable the hydrations flag in the config/telescope.php file like so:

Watchers\ModelWatcher::class => [
    'enabled' => env('TELESCOPE_MODEL_WATCHER', true),
    'events' => ['eloquent.*'],
    'hydrations' => true,
],

Index Screen (The retrieved is the one for model hydrations)

hydrations

Preview Screen

hydrations-preview

Related Entries

hydrations-related-entries

Note

This PR would need a re-build and I haven't committed the compiled files (just the source) as per the contribution guidelines.


Telescope::recordModelEvent($this->hydrationEntries[$modelClass]);
} else {
$this->hydrationEntries[$modelClass]->content['count']++;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm getting an "Illegal string offset 'count'" in this line. I think is because for some reason... content doesn't have an array in some models...

It can be prevent by

            if (! is_array($this->hydrationEntries[$modelClass]->content)) {
                $this->hydrationEntries[$modelClass]->content = json_decode($this->hydrationEntries[$modelClass]->content, true);
            }
            
            $this->hydrationEntries[$modelClass]->content['count']++;

@paras-malhotra
Copy link
Contributor Author

@gboquizosanchez content always seems to be an array. Refer

/**
* Create a new incoming entry instance.
*
* @param array $content
* @param string|null $uuid
* @return void
*/
public function __construct(array $content, $uuid = null)
{
$this->uuid = $uuid ?: (string) Str::orderedUuid();
$this->recordedAt = now();
$this->content = array_merge($content, ['hostname' => gethostname()]);
// $this->tags = ['hostname:'.gethostname()];
}

Can you open an issue with some steps to replicate your error?

@gboquizosanchez
Copy link

Yeah, sure, I'll test in a fresh installation with same parameters and if I can reproduce the issue, I'll post it.

@azaricstefan
Copy link

@gboquizosanchez
@paras-malhotra

Laravel 8.25.0
Telescope 4.4.0

I am getting this error:

ERROR: Illegal string offset 'count'
[2021-02-09T20:36:41.095047+01:00] main-channel.ERROR: Illegal string offset 'count' {"exception":"[object] (ErrorException(code: 0): Illegal string offset 'count' at /var/www/vendor/laravel/telescope/src/Watchers/ModelWatcher.php:87)\n[previous exception] [object] (ErrorException(code: 0): Illegal string offset 'count' at /var/www/vendor/laravel/telescope/src/Watchers/ModelWatcher.php:87)"} []

@paras-malhotra
Copy link
Contributor Author

@azaricstefan can you please share the full stack trace? I'm unable to reproduce this.

@azaricstefan
Copy link

Sure, @paras-malhotra.

Message
Illegal string offset 'count'
Level
ERROR

Here is the stack trace:

/var/www/vendor/laravel/telescope/src/Watchers/ModelWatcher.php:87
/var/www/vendor/laravel/telescope/src/Watchers/ModelWatcher.php:49
/var/www/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:389
/var/www/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php:237
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php:189
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:434
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:328
:
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:327
/var/www/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php:23
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1885
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:571
/var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php:555
/var/www/app/Jobs/SendExceptionNotificationToDevelopers.php:71
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php:40
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php:610
/var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:128
/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:128
/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:103
/var/www/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php:132
/var/www/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:118
/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:128
/var/www/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:103
/var/www/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:120
/var/www/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php:70
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php:98
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:406
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:356
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Worker.php:158
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:116
/var/www/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php:100
/var/www/vendor/laravel/horizon/src/Console/WorkCommand.php:50
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:36
/var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php:40
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:93
/var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php:37
/var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php:610
/var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php:136
/var/www/vendor/symfony/console/Command/Command.php:255
/var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php:121
/var/www/vendor/symfony/console/Application.php:971
/var/www/vendor/symfony/console/Application.php:290
/var/www/vendor/symfony/console/Application.php:166
/var/www/vendor/laravel/framework/src/Illuminate/Console/Application.php:93
/var/www/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:129
/var/www/artisan:37

This command is linked to this exception in Telescope:
{
"command": "horizon:work",
"connection": "redis"
}
options:
{
"name": "default",
"delay": "0",
"backoff": "0",
"max-jobs": "0",
"max-time": "0",
"daemon": false,
"force": false,
"memory": "128",
"once": false,
"stop-when-empty": false,
"queue": "default",
"sleep": "3",
"supervisor": "a2f68ef99676-1BDs:supervisor",
"timeout": "60",
"tries": "3",
"help": false,
"quiet": false,
"verbose": false,
"version": false,
"ansi": false,
"no-ansi": false,
"no-interaction": false,
"env": null
}

This class SendExceptionNotificationToDevelopers.php takes exception details and send it via email.
In TelescopeServiceProvider that Job is dispatched.

        Telescope::afterStoring(function(array $entries, $batchId) {
            foreach ($entries as $entry) {
                /** @var IncomingExceptionEntry $entry */
                if ($entry instanceof IncomingExceptionEntry) {
                    dispatch(new SendExceptionNotificationToDevelopers($entry->uuid, $batchId,
                        $entry->exception->getMessage(),$entry->exception->getFile(), $entry->exception->getLine()));
                }
            }
        });

I hope this helps in the investigation.

@paras-malhotra
Copy link
Contributor Author

@azaricstefan can you share what line 71 of SendExceptionNotificationToDevelopers is doing?

@azaricstefan
Copy link

azaricstefan commented Feb 11, 2021

Sure.
It's hardcoded for first 3 users (devs).

$devs = User::whereIn('id',[1,2,3],'OR')->get();

@paras-malhotra
Copy link
Contributor Author

@azaricstefan, I am unable to reproduce this. I tried dispatching a job within the afterStoring Telescope callback. The job has the exact same query that you gave. But I still didn't get any exceptions.

It would really help if you can create a reproducible exception on a fresh Laravel install and open a new issue for that.

@azaricstefan
Copy link

Ok, I will try to reproduce it on a clean Laravel project.

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

Successfully merging this pull request may close these issues.

4 participants