1565 lines
39 KiB
Markdown
1565 lines
39 KiB
Markdown
# Laravel Pulse
|
||
|
||
* Introduction
|
||
* Installation
|
||
* Configuration
|
||
* Dashboard
|
||
* Authorization
|
||
* Customization
|
||
* Resolving Users
|
||
* Cards
|
||
* Capturing Entries
|
||
* Recorders
|
||
* Filtering
|
||
* Performance
|
||
* Using a Different Database
|
||
* Redis Ingest
|
||
* Sampling
|
||
* Trimming
|
||
* Handling Pulse Exceptions
|
||
* Custom Cards
|
||
* Card Components
|
||
* Styling
|
||
* Data Capture and Aggregation
|
||
|
||
## Introduction
|
||
|
||
[Laravel Pulse](https://github.com/laravel/pulse) delivers at-a-glance
|
||
insights into your application's performance and usage. With Pulse, you can
|
||
track down bottlenecks like slow jobs and endpoints, find your most active
|
||
users, and more.
|
||
|
||
For in-depth debugging of individual events, check out [Laravel
|
||
Telescope](/docs/12.x/telescope).
|
||
|
||
## Installation
|
||
|
||
Pulse's first-party storage implementation currently requires a MySQL,
|
||
MariaDB, or PostgreSQL database. If you are using a different database engine,
|
||
you will need a separate MySQL, MariaDB, or PostgreSQL database for your Pulse
|
||
data.
|
||
|
||
You may install Pulse using the Composer package manager:
|
||
|
||
|
||
|
||
1composer require laravel/pulse
|
||
|
||
|
||
composer require laravel/pulse
|
||
|
||
Next, you should publish the Pulse configuration and migration files using the
|
||
`vendor:publish` Artisan command:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
|
||
|
||
|
||
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
|
||
|
||
Finally, you should run the `migrate` command in order to create the tables
|
||
needed to store Pulse's data:
|
||
|
||
|
||
|
||
1php artisan migrate
|
||
|
||
|
||
php artisan migrate
|
||
|
||
Once Pulse's database migrations have been run, you may access the Pulse
|
||
dashboard via the `/pulse` route.
|
||
|
||
If you do not want to store Pulse data in your application's primary database,
|
||
you may specify a dedicated database connection.
|
||
|
||
### Configuration
|
||
|
||
Many of Pulse's configuration options can be controlled using environment
|
||
variables. To see the available options, register new recorders, or configure
|
||
advanced options, you may publish the `config/pulse.php` configuration file:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --tag=pulse-config
|
||
|
||
|
||
php artisan vendor:publish --tag=pulse-config
|
||
|
||
## Dashboard
|
||
|
||
### Authorization
|
||
|
||
The Pulse dashboard may be accessed via the `/pulse` route. By default, you
|
||
will only be able to access this dashboard in the `local` environment, so you
|
||
will need to configure authorization for your production environments by
|
||
customizing the `'viewPulse'` authorization gate. You can accomplish this
|
||
within your application's `app/Providers/AppServiceProvider.php` file:
|
||
|
||
|
||
|
||
1use App\Models\User;
|
||
|
||
2use Illuminate\Support\Facades\Gate;
|
||
|
||
3
|
||
|
||
4/**
|
||
|
||
5 * Bootstrap any application services.
|
||
|
||
6 */
|
||
|
||
7public function boot(): void
|
||
|
||
8{
|
||
|
||
9 Gate::define('viewPulse', function (User $user) {
|
||
|
||
10 return $user->isAdmin();
|
||
|
||
11 });
|
||
|
||
12
|
||
|
||
13 // ...
|
||
|
||
14}
|
||
|
||
|
||
use App\Models\User;
|
||
use Illuminate\Support\Facades\Gate;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
Gate::define('viewPulse', function (User $user) {
|
||
return $user->isAdmin();
|
||
});
|
||
|
||
// ...
|
||
}
|
||
|
||
### Customization
|
||
|
||
The Pulse dashboard cards and layout may be configured by publishing the
|
||
dashboard view. The dashboard view will be published to
|
||
`resources/views/vendor/pulse/dashboard.blade.php`:
|
||
|
||
|
||
|
||
1php artisan vendor:publish --tag=pulse-dashboard
|
||
|
||
|
||
php artisan vendor:publish --tag=pulse-dashboard
|
||
|
||
The dashboard is powered by [Livewire](https://livewire.laravel.com/), and
|
||
allows you to customize the cards and layout without needing to rebuild any
|
||
JavaScript assets.
|
||
|
||
Within this file, the `<x-pulse>` component is responsible for rendering the
|
||
dashboard and provides a grid layout for the cards. If you would like the
|
||
dashboard to span the full width of the screen, you may provide the `full-
|
||
width` prop to the component:
|
||
|
||
|
||
|
||
1<x-pulse full-width>
|
||
|
||
2 ...
|
||
|
||
3</x-pulse>
|
||
|
||
|
||
<x-pulse full-width>
|
||
...
|
||
</x-pulse>
|
||
|
||
By default, the `<x-pulse>` component will create a 12 column grid, but you
|
||
may customize this using the `cols` prop:
|
||
|
||
|
||
|
||
1<x-pulse cols="16">
|
||
|
||
2 ...
|
||
|
||
3</x-pulse>
|
||
|
||
|
||
<x-pulse cols="16">
|
||
...
|
||
</x-pulse>
|
||
|
||
Each card accepts a `cols` and `rows` prop to control the space and
|
||
positioning:
|
||
|
||
|
||
|
||
1<livewire:pulse.usage cols="4" rows="2" />
|
||
|
||
|
||
<livewire:pulse.usage cols="4" rows="2" />
|
||
|
||
Most cards also accept an `expand` prop to show the full card instead of
|
||
scrolling:
|
||
|
||
|
||
|
||
1<livewire:pulse.slow-queries expand />
|
||
|
||
|
||
<livewire:pulse.slow-queries expand />
|
||
|
||
### Resolving Users
|
||
|
||
For cards that display information about your users, such as the Application
|
||
Usage card, Pulse will only record the user's ID. When rendering the
|
||
dashboard, Pulse will resolve the `name` and `email` fields from your default
|
||
`Authenticatable` model and display avatars using the Gravatar web service.
|
||
|
||
You may customize the fields and avatar by invoking the `Pulse::user` method
|
||
within your application's `App\Providers\AppServiceProvider` class.
|
||
|
||
The `user` method accepts a closure which will receive the `Authenticatable`
|
||
model to be displayed and should return an array containing `name`, `extra`,
|
||
and `avatar` information for the user:
|
||
|
||
|
||
|
||
1use Laravel\Pulse\Facades\Pulse;
|
||
|
||
2
|
||
|
||
3/**
|
||
|
||
4 * Bootstrap any application services.
|
||
|
||
5 */
|
||
|
||
6public function boot(): void
|
||
|
||
7{
|
||
|
||
8 Pulse::user(fn ($user) => [
|
||
|
||
9 'name' => $user->name,
|
||
|
||
10 'extra' => $user->email,
|
||
|
||
11 'avatar' => $user->avatar_url,
|
||
|
||
12 ]);
|
||
|
||
13
|
||
|
||
14 // ...
|
||
|
||
15}
|
||
|
||
|
||
use Laravel\Pulse\Facades\Pulse;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
Pulse::user(fn ($user) => [
|
||
'name' => $user->name,
|
||
'extra' => $user->email,
|
||
'avatar' => $user->avatar_url,
|
||
]);
|
||
|
||
// ...
|
||
}
|
||
|
||
You may completely customize how the authenticated user is captured and
|
||
retrieved by implementing the `Laravel\Pulse\Contracts\ResolvesUsers` contract
|
||
and binding it in Laravel's [service container](/docs/12.x/container#binding-
|
||
a-singleton).
|
||
|
||
### Cards
|
||
|
||
#### Servers
|
||
|
||
The `<livewire:pulse.servers />` card displays system resource usage for all
|
||
servers running the `pulse:check` command. Please refer to the documentation
|
||
regarding the servers recorder for more information on system resource
|
||
reporting.
|
||
|
||
If you replace a server in your infrastructure, you may wish to stop
|
||
displaying the inactive server in the Pulse dashboard after a given duration.
|
||
You may accomplish this using the `ignore-after` prop, which accepts the
|
||
number of seconds after which inactive servers should be removed from the
|
||
Pulse dashboard. Alternatively, you may provide a relative time formatted
|
||
string, such as `1 hour` or `3 days and 1 hour`:
|
||
|
||
|
||
|
||
1<livewire:pulse.servers ignore-after="3 hours" />
|
||
|
||
|
||
<livewire:pulse.servers ignore-after="3 hours" />
|
||
|
||
#### Application Usage
|
||
|
||
The `<livewire:pulse.usage />` card displays the top 10 users making requests
|
||
to your application, dispatching jobs, and experiencing slow requests.
|
||
|
||
If you wish to view all usage metrics on screen at the same time, you may
|
||
include the card multiple times and specify the `type` attribute:
|
||
|
||
|
||
|
||
1<livewire:pulse.usage type="requests" />
|
||
|
||
2<livewire:pulse.usage type="slow_requests" />
|
||
|
||
3<livewire:pulse.usage type="jobs" />
|
||
|
||
|
||
<livewire:pulse.usage type="requests" />
|
||
<livewire:pulse.usage type="slow_requests" />
|
||
<livewire:pulse.usage type="jobs" />
|
||
|
||
To learn how to customize how Pulse retrieves and displays user information,
|
||
consult our documentation on resolving users.
|
||
|
||
If your application receives a lot of requests or dispatches a lot of jobs,
|
||
you may wish to enable sampling. See the user requests recorder, user jobs
|
||
recorder, and slow jobs recorder documentation for more information.
|
||
|
||
#### Exceptions
|
||
|
||
The `<livewire:pulse.exceptions />` card shows the frequency and recency of
|
||
exceptions occurring in your application. By default, exceptions are grouped
|
||
based on the exception class and location where it occurred. See the
|
||
exceptions recorder documentation for more information.
|
||
|
||
#### Queues
|
||
|
||
The `<livewire:pulse.queues />` card shows the throughput of the queues in
|
||
your application, including the number of jobs queued, processing, processed,
|
||
released, and failed. See the queues recorder documentation for more
|
||
information.
|
||
|
||
#### Slow Requests
|
||
|
||
The `<livewire:pulse.slow-requests />` card shows incoming requests to your
|
||
application that exceed the configured threshold, which is 1,000ms by default.
|
||
See the slow requests recorder documentation for more information.
|
||
|
||
#### Slow Jobs
|
||
|
||
The `<livewire:pulse.slow-jobs />` card shows the queued jobs in your
|
||
application that exceed the configured threshold, which is 1,000ms by default.
|
||
See the slow jobs recorder documentation for more information.
|
||
|
||
#### Slow Queries
|
||
|
||
The `<livewire:pulse.slow-queries />` card shows the database queries in your
|
||
application that exceed the configured threshold, which is 1,000ms by default.
|
||
|
||
By default, slow queries are grouped based on the SQL query (without bindings)
|
||
and the location where it occurred, but you may choose to not capture the
|
||
location if you wish to group solely on the SQL query.
|
||
|
||
If you encounter rendering performance issues due to extremely large SQL
|
||
queries receiving syntax highlighting, you may disable highlighting by adding
|
||
the `without-highlighting` prop:
|
||
|
||
|
||
|
||
1<livewire:pulse.slow-queries without-highlighting />
|
||
|
||
|
||
<livewire:pulse.slow-queries without-highlighting />
|
||
|
||
See the slow queries recorder documentation for more information.
|
||
|
||
#### Slow Outgoing Requests
|
||
|
||
The `<livewire:pulse.slow-outgoing-requests />` card shows outgoing requests
|
||
made using Laravel's [HTTP client](/docs/12.x/http-client) that exceed the
|
||
configured threshold, which is 1,000ms by default.
|
||
|
||
By default, entries will be grouped by the full URL. However, you may wish to
|
||
normalize or group similar outgoing requests using regular expressions. See
|
||
the slow outgoing requests recorder documentation for more information.
|
||
|
||
#### Cache
|
||
|
||
The `<livewire:pulse.cache />` card shows the cache hit and miss statistics
|
||
for your application, both globally and for individual keys.
|
||
|
||
By default, entries will be grouped by key. However, you may wish to normalize
|
||
or group similar keys using regular expressions. See the cache interactions
|
||
recorder documentation for more information.
|
||
|
||
## Capturing Entries
|
||
|
||
Most Pulse recorders will automatically capture entries based on framework
|
||
events dispatched by Laravel. However, the servers recorder and some third-
|
||
party cards must poll for information regularly. To use these cards, you must
|
||
run the `pulse:check` daemon on all of your individual application servers:
|
||
|
||
|
||
|
||
1php artisan pulse:check
|
||
|
||
|
||
php artisan pulse:check
|
||
|
||
To keep the `pulse:check` process running permanently in the background, you
|
||
should use a process monitor such as Supervisor to ensure that the command
|
||
does not stop running.
|
||
|
||
As the `pulse:check` command is a long-lived process, it will not see changes
|
||
to your codebase without being restarted. You should gracefully restart the
|
||
command by calling the `pulse:restart` command during your application's
|
||
deployment process:
|
||
|
||
|
||
|
||
1php artisan pulse:restart
|
||
|
||
|
||
php artisan pulse:restart
|
||
|
||
Pulse uses the [cache](/docs/12.x/cache) to store restart signals, so you
|
||
should verify that a cache driver is properly configured for your application
|
||
before using this feature.
|
||
|
||
### Recorders
|
||
|
||
Recorders are responsible for capturing entries from your application to be
|
||
recorded in the Pulse database. Recorders are registered and configured in the
|
||
`recorders` section of the Pulse configuration file.
|
||
|
||
#### Cache Interactions
|
||
|
||
The `CacheInteractions` recorder captures information about the
|
||
[cache](/docs/12.x/cache) hits and misses occurring in your application for
|
||
display on the Cache card.
|
||
|
||
You may optionally adjust the sample rate and ignored key patterns.
|
||
|
||
You may also configure key grouping so that similar keys are grouped as a
|
||
single entry. For example, you may wish to remove unique IDs from keys caching
|
||
the same type of information. Groups are configured using a regular expression
|
||
to "find and replace" parts of the key. An example is included in the
|
||
configuration file:
|
||
|
||
|
||
|
||
1Recorders\CacheInteractions::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'groups' => [
|
||
|
||
4 // '/:\d+/' => ':*',
|
||
|
||
5 ],
|
||
|
||
6],
|
||
|
||
|
||
Recorders\CacheInteractions::class => [
|
||
// ...
|
||
'groups' => [
|
||
// '/:\d+/' => ':*',
|
||
],
|
||
],
|
||
|
||
The first pattern that matches will be used. If no patterns match, then the
|
||
key will be captured as-is.
|
||
|
||
#### Exceptions
|
||
|
||
The `Exceptions` recorder captures information about reportable exceptions
|
||
occurring in your application for display on the Exceptions card.
|
||
|
||
You may optionally adjust the sample rate and ignored exceptions patterns. You
|
||
may also configure whether to capture the location that the exception
|
||
originated from. The captured location will be displayed on the Pulse
|
||
dashboard which can help to track down the exception origin; however, if the
|
||
same exception occurs in multiple locations then it will appear multiple times
|
||
for each unique location.
|
||
|
||
#### Queues
|
||
|
||
The `Queues` recorder captures information about your applications queues for
|
||
display on the Queues.
|
||
|
||
You may optionally adjust the sample rate and ignored jobs patterns.
|
||
|
||
#### Slow Jobs
|
||
|
||
The `SlowJobs` recorder captures information about slow jobs occurring in your
|
||
application for display on the Slow Jobs card.
|
||
|
||
You may optionally adjust the slow job threshold, sample rate, and ignored job
|
||
patterns.
|
||
|
||
You may have some jobs that you expect to take longer than others. In those
|
||
cases, you may configure per-job thresholds:
|
||
|
||
|
||
|
||
1Recorders\SlowJobs::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'threshold' => [
|
||
|
||
4 '#^App\\Jobs\\GenerateYearlyReports$#' => 5000,
|
||
|
||
5 'default' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
|
||
|
||
6 ],
|
||
|
||
7],
|
||
|
||
|
||
Recorders\SlowJobs::class => [
|
||
// ...
|
||
'threshold' => [
|
||
'#^App\\Jobs\\GenerateYearlyReports$#' => 5000,
|
||
'default' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
|
||
],
|
||
],
|
||
|
||
If no regular expression patterns match the job's classname, then the
|
||
`'default'` value will be used.
|
||
|
||
#### Slow Outgoing Requests
|
||
|
||
The `SlowOutgoingRequests` recorder captures information about outgoing HTTP
|
||
requests made using Laravel's [HTTP client](/docs/12.x/http-client) that
|
||
exceed the configured threshold for display on the Slow Outgoing Requests
|
||
card.
|
||
|
||
You may optionally adjust the slow outgoing request threshold, sample rate,
|
||
and ignored URL patterns.
|
||
|
||
You may have some outgoing requests that you expect to take longer than
|
||
others. In those cases, you may configure per-request thresholds:
|
||
|
||
|
||
|
||
1Recorders\SlowOutgoingRequests::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'threshold' => [
|
||
|
||
4 '#backup.zip$#' => 5000,
|
||
|
||
5 'default' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
|
||
|
||
6 ],
|
||
|
||
7],
|
||
|
||
|
||
Recorders\SlowOutgoingRequests::class => [
|
||
// ...
|
||
'threshold' => [
|
||
'#backup.zip$#' => 5000,
|
||
'default' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
|
||
],
|
||
],
|
||
|
||
If no regular expression patterns match the request's URL, then the
|
||
`'default'` value will be used.
|
||
|
||
You may also configure URL grouping so that similar URLs are grouped as a
|
||
single entry. For example, you may wish to remove unique IDs from URL paths or
|
||
group by domain only. Groups are configured using a regular expression to
|
||
"find and replace" parts of the URL. Some examples are included in the
|
||
configuration file:
|
||
|
||
|
||
|
||
1Recorders\SlowOutgoingRequests::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'groups' => [
|
||
|
||
4 // '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
|
||
|
||
5 // '#^https?://([^/]*).*$#' => '\1',
|
||
|
||
6 // '#/\d+#' => '/*',
|
||
|
||
7 ],
|
||
|
||
8],
|
||
|
||
|
||
Recorders\SlowOutgoingRequests::class => [
|
||
// ...
|
||
'groups' => [
|
||
// '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
|
||
// '#^https?://([^/]*).*$#' => '\1',
|
||
// '#/\d+#' => '/*',
|
||
],
|
||
],
|
||
|
||
The first pattern that matches will be used. If no patterns match, then the
|
||
URL will be captured as-is.
|
||
|
||
#### Slow Queries
|
||
|
||
The `SlowQueries` recorder captures any database queries in your application
|
||
that exceed the configured threshold for display on the Slow Queries card.
|
||
|
||
You may optionally adjust the slow query threshold, sample rate, and ignored
|
||
query patterns. You may also configure whether to capture the query location.
|
||
The captured location will be displayed on the Pulse dashboard which can help
|
||
to track down the query origin; however, if the same query is made in multiple
|
||
locations then it will appear multiple times for each unique location.
|
||
|
||
You may have some queries that you expect to take longer than others. In those
|
||
cases, you may configure per-query thresholds:
|
||
|
||
|
||
|
||
1Recorders\SlowQueries::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'threshold' => [
|
||
|
||
4 '#^insert into `yearly_reports`#' => 5000,
|
||
|
||
5 'default' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
|
||
|
||
6 ],
|
||
|
||
7],
|
||
|
||
|
||
Recorders\SlowQueries::class => [
|
||
// ...
|
||
'threshold' => [
|
||
'#^insert into `yearly_reports`#' => 5000,
|
||
'default' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
|
||
],
|
||
],
|
||
|
||
If no regular expression patterns match the query's SQL, then the `'default'`
|
||
value will be used.
|
||
|
||
#### Slow Requests
|
||
|
||
The `Requests` recorder captures information about requests made to your
|
||
application for display on the Slow Requests and Application Usage cards.
|
||
|
||
You may optionally adjust the slow route threshold, sample rate, and ignored
|
||
paths.
|
||
|
||
You may have some requests that you expect to take longer than others. In
|
||
those cases, you may configure per-request thresholds:
|
||
|
||
|
||
|
||
1Recorders\SlowRequests::class => [
|
||
|
||
2 // ...
|
||
|
||
3 'threshold' => [
|
||
|
||
4 '#^/admin/#' => 5000,
|
||
|
||
5 'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
|
||
|
||
6 ],
|
||
|
||
7],
|
||
|
||
|
||
Recorders\SlowRequests::class => [
|
||
// ...
|
||
'threshold' => [
|
||
'#^/admin/#' => 5000,
|
||
'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
|
||
],
|
||
],
|
||
|
||
If no regular expression patterns match the request's URL, then the
|
||
`'default'` value will be used.
|
||
|
||
#### Servers
|
||
|
||
The `Servers` recorder captures CPU, memory, and storage usage of the servers
|
||
that power your application for display on the Servers card. This recorder
|
||
requires the pulse:check command to be running on each of the servers you wish
|
||
to monitor.
|
||
|
||
Each reporting server must have a unique name. By default, Pulse will use the
|
||
value returned by PHP's `gethostname` function. If you wish to customize this,
|
||
you may set the `PULSE_SERVER_NAME` environment variable:
|
||
|
||
|
||
|
||
1PULSE_SERVER_NAME=load-balancer
|
||
|
||
|
||
PULSE_SERVER_NAME=load-balancer
|
||
|
||
The Pulse configuration file also allows you to customize the directories that
|
||
are monitored.
|
||
|
||
#### User Jobs
|
||
|
||
The `UserJobs` recorder captures information about the users dispatching jobs
|
||
in your application for display on the Application Usage card.
|
||
|
||
You may optionally adjust the sample rate and ignored job patterns.
|
||
|
||
#### User Requests
|
||
|
||
The `UserRequests` recorder captures information about the users making
|
||
requests to your application for display on the Application Usage card.
|
||
|
||
You may optionally adjust the sample rate and ignored URL patterns.
|
||
|
||
### Filtering
|
||
|
||
As we have seen, many recorders offer the ability to, via configuration,
|
||
"ignore" incoming entries based on their value, such as a request's URL. But,
|
||
sometimes it may be useful to filter out records based on other factors, such
|
||
as the currently authenticated user. To filter out these records, you may pass
|
||
a closure to Pulse's `filter` method. Typically, the `filter` method should be
|
||
invoked within the `boot` method of your application's `AppServiceProvider`:
|
||
|
||
|
||
|
||
1use Illuminate\Support\Facades\Auth;
|
||
|
||
2use Laravel\Pulse\Entry;
|
||
|
||
3use Laravel\Pulse\Facades\Pulse;
|
||
|
||
4use Laravel\Pulse\Value;
|
||
|
||
5
|
||
|
||
6/**
|
||
|
||
7 * Bootstrap any application services.
|
||
|
||
8 */
|
||
|
||
9public function boot(): void
|
||
|
||
10{
|
||
|
||
11 Pulse::filter(function (Entry|Value $entry) {
|
||
|
||
12 return Auth::user()->isNotAdmin();
|
||
|
||
13 });
|
||
|
||
14
|
||
|
||
15 // ...
|
||
|
||
16}
|
||
|
||
|
||
use Illuminate\Support\Facades\Auth;
|
||
use Laravel\Pulse\Entry;
|
||
use Laravel\Pulse\Facades\Pulse;
|
||
use Laravel\Pulse\Value;
|
||
|
||
/**
|
||
* Bootstrap any application services.
|
||
*/
|
||
public function boot(): void
|
||
{
|
||
Pulse::filter(function (Entry|Value $entry) {
|
||
return Auth::user()->isNotAdmin();
|
||
});
|
||
|
||
// ...
|
||
}
|
||
|
||
## Performance
|
||
|
||
Pulse has been designed to drop into an existing application without requiring
|
||
any additional infrastructure. However, for high-traffic applications, there
|
||
are several ways of removing any impact Pulse may have on your application's
|
||
performance.
|
||
|
||
### Using a Different Database
|
||
|
||
For high-traffic applications, you may prefer to use a dedicated database
|
||
connection for Pulse to avoid impacting your application database.
|
||
|
||
You may customize the [database connection](/docs/12.x/database#configuration)
|
||
used by Pulse by setting the `PULSE_DB_CONNECTION` environment variable.
|
||
|
||
|
||
|
||
1PULSE_DB_CONNECTION=pulse
|
||
|
||
|
||
PULSE_DB_CONNECTION=pulse
|
||
|
||
### Redis Ingest
|
||
|
||
The Redis Ingest requires Redis 6.2 or greater and `phpredis` or `predis` as
|
||
the application's configured Redis client driver.
|
||
|
||
By default, Pulse will store entries directly to the configured database
|
||
connection after the HTTP response has been sent to the client or a job has
|
||
been processed; however, you may use Pulse's Redis ingest driver to send
|
||
entries to a Redis stream instead. This can be enabled by configuring the
|
||
`PULSE_INGEST_DRIVER` environment variable:
|
||
|
||
|
||
|
||
1PULSE_INGEST_DRIVER=redis
|
||
|
||
|
||
PULSE_INGEST_DRIVER=redis
|
||
|
||
Pulse will use your default [Redis connection](/docs/12.x/redis#configuration)
|
||
by default, but you may customize this via the `PULSE_REDIS_CONNECTION`
|
||
environment variable:
|
||
|
||
|
||
|
||
1PULSE_REDIS_CONNECTION=pulse
|
||
|
||
|
||
PULSE_REDIS_CONNECTION=pulse
|
||
|
||
When using the Redis ingest driver, your Pulse installation should always use
|
||
a different Redis connection than your Redis powered queue, if applicable.
|
||
|
||
When using the Redis ingest, you will need to run the `pulse:work` command to
|
||
monitor the stream and move entries from Redis into Pulse's database tables.
|
||
|
||
|
||
|
||
1php artisan pulse:work
|
||
|
||
|
||
php artisan pulse:work
|
||
|
||
To keep the `pulse:work` process running permanently in the background, you
|
||
should use a process monitor such as Supervisor to ensure that the Pulse
|
||
worker does not stop running.
|
||
|
||
As the `pulse:work` command is a long-lived process, it will not see changes
|
||
to your codebase without being restarted. You should gracefully restart the
|
||
command by calling the `pulse:restart` command during your application's
|
||
deployment process:
|
||
|
||
|
||
|
||
1php artisan pulse:restart
|
||
|
||
|
||
php artisan pulse:restart
|
||
|
||
Pulse uses the [cache](/docs/12.x/cache) to store restart signals, so you
|
||
should verify that a cache driver is properly configured for your application
|
||
before using this feature.
|
||
|
||
### Sampling
|
||
|
||
By default, Pulse will capture every relevant event that occurs in your
|
||
application. For high-traffic applications, this can result in needing to
|
||
aggregate millions of database rows in the dashboard, especially for longer
|
||
time periods.
|
||
|
||
You may instead choose to enable "sampling" on certain Pulse data recorders.
|
||
For example, setting the sample rate to `0.1` on the User Requests recorder
|
||
will mean that you only record approximately 10% of the requests to your
|
||
application. In the dashboard, the values will be scaled up and prefixed with
|
||
a `~` to indicate that they are an approximation.
|
||
|
||
In general, the more entries you have for a particular metric, the lower you
|
||
can safely set the sample rate without sacrificing too much accuracy.
|
||
|
||
### Trimming
|
||
|
||
Pulse will automatically trim its stored entries once they are outside of the
|
||
dashboard window. Trimming occurs when ingesting data using a lottery system
|
||
which may be customized in the Pulse configuration file.
|
||
|
||
### Handling Pulse Exceptions
|
||
|
||
If an exception occurs while capturing Pulse data, such as being unable to
|
||
connect to the storage database, Pulse will silently fail to avoid impacting
|
||
your application.
|
||
|
||
If you wish to customize how these exceptions are handled, you may provide a
|
||
closure to the `handleExceptionsUsing` method:
|
||
|
||
|
||
|
||
1use Laravel\Pulse\Facades\Pulse;
|
||
|
||
2use Illuminate\Support\Facades\Log;
|
||
|
||
3
|
||
|
||
4Pulse::handleExceptionsUsing(function ($e) {
|
||
|
||
5 Log::debug('An exception happened in Pulse', [
|
||
|
||
6 'message' => $e->getMessage(),
|
||
|
||
7 'stack' => $e->getTraceAsString(),
|
||
|
||
8 ]);
|
||
|
||
9});
|
||
|
||
|
||
use Laravel\Pulse\Facades\Pulse;
|
||
use Illuminate\Support\Facades\Log;
|
||
|
||
Pulse::handleExceptionsUsing(function ($e) {
|
||
Log::debug('An exception happened in Pulse', [
|
||
'message' => $e->getMessage(),
|
||
'stack' => $e->getTraceAsString(),
|
||
]);
|
||
});
|
||
|
||
## Custom Cards
|
||
|
||
Pulse allows you to build custom cards to display data relevant to your
|
||
application's specific needs. Pulse uses
|
||
[Livewire](https://livewire.laravel.com), so you may want to [review its
|
||
documentation](https://livewire.laravel.com/docs) before building your first
|
||
custom card.
|
||
|
||
### Card Components
|
||
|
||
Creating a custom card in Laravel Pulse starts with extending the base `Card`
|
||
Livewire component and defining a corresponding view:
|
||
|
||
|
||
|
||
1namespace App\Livewire\Pulse;
|
||
|
||
2
|
||
|
||
3use Laravel\Pulse\Livewire\Card;
|
||
|
||
4use Livewire\Attributes\Lazy;
|
||
|
||
5
|
||
|
||
6#[Lazy]
|
||
|
||
7class TopSellers extends Card
|
||
|
||
8{
|
||
|
||
9 public function render()
|
||
|
||
10 {
|
||
|
||
11 return view('livewire.pulse.top-sellers');
|
||
|
||
12 }
|
||
|
||
13}
|
||
|
||
|
||
namespace App\Livewire\Pulse;
|
||
|
||
use Laravel\Pulse\Livewire\Card;
|
||
use Livewire\Attributes\Lazy;
|
||
|
||
#[Lazy]
|
||
class TopSellers extends Card
|
||
{
|
||
public function render()
|
||
{
|
||
return view('livewire.pulse.top-sellers');
|
||
}
|
||
}
|
||
|
||
When using Livewire's [lazy loading](https://livewire.laravel.com/docs/lazy)
|
||
feature, The `Card` component will automatically provide a placeholder that
|
||
respects the `cols` and `rows` attributes passed to your component.
|
||
|
||
When writing your Pulse card's corresponding view, you may leverage Pulse's
|
||
Blade components for a consistent look and feel:
|
||
|
||
|
||
|
||
1<x-pulse::card :cols="$cols" :rows="$rows" :class="$class" wire:poll.5s="">
|
||
|
||
2 <x-pulse::card-header name="Top Sellers">
|
||
|
||
3 <x-slot:icon>
|
||
|
||
4 ...
|
||
|
||
5 </x-slot:icon>
|
||
|
||
6 </x-pulse::card-header>
|
||
|
||
7
|
||
|
||
8 <x-pulse::scroll :expand="$expand">
|
||
|
||
9 ...
|
||
|
||
10 </x-pulse::scroll>
|
||
|
||
11</x-pulse::card>
|
||
|
||
|
||
<x-pulse::card :cols="$cols" :rows="$rows" :class="$class" wire:poll.5s="">
|
||
<x-pulse::card-header name="Top Sellers">
|
||
<x-slot:icon>
|
||
...
|
||
</x-slot:icon>
|
||
</x-pulse::card-header>
|
||
|
||
<x-pulse::scroll :expand="$expand">
|
||
...
|
||
</x-pulse::scroll>
|
||
</x-pulse::card>
|
||
|
||
The `$cols`, `$rows`, `$class`, and `$expand` variables should be passed to
|
||
their respective Blade components so the card layout may be customized from
|
||
the dashboard view. You may also wish to include the `wire:poll.5s=""`
|
||
attribute in your view to have the card automatically update.
|
||
|
||
Once you have defined your Livewire component and template, the card may be
|
||
included in your dashboard view:
|
||
|
||
|
||
|
||
1<x-pulse>
|
||
|
||
2 ...
|
||
|
||
3
|
||
|
||
4 <livewire:pulse.top-sellers cols="4" />
|
||
|
||
5</x-pulse>
|
||
|
||
|
||
<x-pulse>
|
||
...
|
||
|
||
<livewire:pulse.top-sellers cols="4" />
|
||
</x-pulse>
|
||
|
||
If your card is included in a package, you will need to register the component
|
||
with Livewire using the `Livewire::component` method.
|
||
|
||
### Styling
|
||
|
||
If your card requires additional styling beyond the classes and components
|
||
included with Pulse, there are a few options for including custom CSS for your
|
||
cards.
|
||
|
||
#### Laravel Vite Integration
|
||
|
||
If your custom card lives within your application's code base and you are
|
||
using Laravel's [Vite integration](/docs/12.x/vite), you may update your
|
||
`vite.config.js` file to include a dedicated CSS entry point for your card:
|
||
|
||
|
||
|
||
1laravel({
|
||
|
||
2 input: [
|
||
|
||
3 'resources/css/pulse/top-sellers.css',
|
||
|
||
4 // ...
|
||
|
||
5 ],
|
||
|
||
6}),
|
||
|
||
|
||
laravel({
|
||
input: [
|
||
'resources/css/pulse/top-sellers.css',
|
||
// ...
|
||
],
|
||
}),
|
||
|
||
You may then use the `@vite` Blade directive in your dashboard view,
|
||
specifying the CSS entrypoint for your card:
|
||
|
||
|
||
|
||
1<x-pulse>
|
||
|
||
2 @vite('resources/css/pulse/top-sellers.css')
|
||
|
||
3
|
||
|
||
4 ...
|
||
|
||
5</x-pulse>
|
||
|
||
|
||
<x-pulse>
|
||
@vite('resources/css/pulse/top-sellers.css')
|
||
|
||
...
|
||
</x-pulse>
|
||
|
||
#### CSS Files
|
||
|
||
For other use cases, including Pulse cards contained within a package, you may
|
||
instruct Pulse to load additional stylesheets by defining a `css` method on
|
||
your Livewire component that returns the file path to your CSS file:
|
||
|
||
|
||
|
||
1class TopSellers extends Card
|
||
|
||
2{
|
||
|
||
3 // ...
|
||
|
||
4
|
||
|
||
5 protected function css()
|
||
|
||
6 {
|
||
|
||
7 return __DIR__.'/../../dist/top-sellers.css';
|
||
|
||
8 }
|
||
|
||
9}
|
||
|
||
|
||
class TopSellers extends Card
|
||
{
|
||
// ...
|
||
|
||
protected function css()
|
||
{
|
||
return __DIR__.'/../../dist/top-sellers.css';
|
||
}
|
||
}
|
||
|
||
When this card is included on the dashboard, Pulse will automatically include
|
||
the contents of this file within a `<style>` tag so it does not need to be
|
||
published to the `public` directory.
|
||
|
||
#### Tailwind CSS
|
||
|
||
When using Tailwind CSS, you should create a dedicated Tailwind configuration
|
||
file to avoid loading unnecessary CSS or conflicting with Pulse's Tailwind
|
||
classes:
|
||
|
||
|
||
|
||
1export default {
|
||
|
||
2 darkMode: 'class',
|
||
|
||
3 important: '#top-sellers',
|
||
|
||
4 content: [
|
||
|
||
5 './resources/views/livewire/pulse/top-sellers.blade.php',
|
||
|
||
6 ],
|
||
|
||
7 corePlugins: {
|
||
|
||
8 preflight: false,
|
||
|
||
9 },
|
||
|
||
10};
|
||
|
||
|
||
export default {
|
||
darkMode: 'class',
|
||
important: '#top-sellers',
|
||
content: [
|
||
'./resources/views/livewire/pulse/top-sellers.blade.php',
|
||
],
|
||
corePlugins: {
|
||
preflight: false,
|
||
},
|
||
};
|
||
|
||
You may then specify the configuration file in your CSS entrypoint:
|
||
|
||
|
||
|
||
1@config "../../tailwind.top-sellers.config.js";
|
||
|
||
2@tailwind base;
|
||
|
||
3@tailwind components;
|
||
|
||
4@tailwind utilities;
|
||
|
||
|
||
@config "../../tailwind.top-sellers.config.js";
|
||
@tailwind base;
|
||
@tailwind components;
|
||
@tailwind utilities;
|
||
|
||
You will also need to include an `id` or `class` attribute in your card's view
|
||
that matches the selector passed to Tailwind's [important selector
|
||
strategy](https://tailwindcss.com/docs/configuration#selector-strategy):
|
||
|
||
|
||
|
||
1<x-pulse::card id="top-sellers" :cols="$cols" :rows="$rows" class="$class">
|
||
|
||
2 ...
|
||
|
||
3</x-pulse::card>
|
||
|
||
|
||
<x-pulse::card id="top-sellers" :cols="$cols" :rows="$rows" class="$class">
|
||
...
|
||
</x-pulse::card>
|
||
|
||
### Data Capture and Aggregation
|
||
|
||
Custom cards may fetch and display data from anywhere; however, you may wish
|
||
to leverage Pulse's powerful and efficient data recording and aggregation
|
||
system.
|
||
|
||
#### Capturing Entries
|
||
|
||
Pulse allows you to record "entries" using the `Pulse::record` method:
|
||
|
||
|
||
|
||
1use Laravel\Pulse\Facades\Pulse;
|
||
|
||
2
|
||
|
||
3Pulse::record('user_sale', $user->id, $sale->amount)
|
||
|
||
4 ->sum()
|
||
|
||
5 ->count();
|
||
|
||
|
||
use Laravel\Pulse\Facades\Pulse;
|
||
|
||
Pulse::record('user_sale', $user->id, $sale->amount)
|
||
->sum()
|
||
->count();
|
||
|
||
The first argument provided to the `record` method is the `type` for the entry
|
||
you are recording, while the second argument is the `key` that determines how
|
||
the aggregated data should be grouped. For most aggregation methods you will
|
||
also need to specify a `value` to be aggregated. In the example above, the
|
||
value being aggregated is `$sale->amount`. You may then invoke one or more
|
||
aggregation methods (such as `sum`) so that Pulse may capture pre-aggregated
|
||
values into "buckets" for efficient retrieval later.
|
||
|
||
The available aggregation methods are:
|
||
|
||
* `avg`
|
||
* `count`
|
||
* `max`
|
||
* `min`
|
||
* `sum`
|
||
|
||
When building a card package that captures the currently authenticated user
|
||
ID, you should use the `Pulse::resolveAuthenticatedUserId()` method, which
|
||
respects any user resolver customizations made to the application.
|
||
|
||
#### Retrieving Aggregate Data
|
||
|
||
When extending Pulse's `Card` Livewire component, you may use the `aggregate`
|
||
method to retrieve aggregated data for the period being viewed in the
|
||
dashboard:
|
||
|
||
|
||
|
||
1class TopSellers extends Card
|
||
|
||
2{
|
||
|
||
3 public function render()
|
||
|
||
4 {
|
||
|
||
5 return view('livewire.pulse.top-sellers', [
|
||
|
||
6 'topSellers' => $this->aggregate('user_sale', ['sum', 'count'])
|
||
|
||
7 ]);
|
||
|
||
8 }
|
||
|
||
9}
|
||
|
||
|
||
class TopSellers extends Card
|
||
{
|
||
public function render()
|
||
{
|
||
return view('livewire.pulse.top-sellers', [
|
||
'topSellers' => $this->aggregate('user_sale', ['sum', 'count'])
|
||
]);
|
||
}
|
||
}
|
||
|
||
The `aggregate` method returns a collection of PHP `stdClass` objects. Each
|
||
object will contain the `key` property captured earlier, along with keys for
|
||
each of the requested aggregates:
|
||
|
||
|
||
|
||
1@foreach ($topSellers as $seller)
|
||
|
||
2 {{ $seller->key }}
|
||
|
||
3 {{ $seller->sum }}
|
||
|
||
4 {{ $seller->count }}
|
||
|
||
5@endforeach
|
||
|
||
|
||
@foreach ($topSellers as $seller)
|
||
{{ $seller->key }}
|
||
{{ $seller->sum }}
|
||
{{ $seller->count }}
|
||
@endforeach
|
||
|
||
Pulse will primarily retrieve data from the pre-aggregated buckets; therefore,
|
||
the specified aggregates must have been captured up-front using the
|
||
`Pulse::record` method. The oldest bucket will typically fall partially
|
||
outside the period, so Pulse will aggregate the oldest entries to fill the gap
|
||
and give an accurate value for the entire period, without needing to aggregate
|
||
the entire period on each poll request.
|
||
|
||
You may also retrieve a total value for a given type by using the
|
||
`aggregateTotal` method. For example, the following method would retrieve the
|
||
total of all user sales instead of grouping them by user.
|
||
|
||
|
||
|
||
1$total = $this->aggregateTotal('user_sale', 'sum');
|
||
|
||
|
||
$total = $this->aggregateTotal('user_sale', 'sum');
|
||
|
||
#### Displaying Users
|
||
|
||
When working with aggregates that record a user ID as the key, you may resolve
|
||
the keys to user records using the `Pulse::resolveUsers` method:
|
||
|
||
|
||
|
||
1$aggregates = $this->aggregate('user_sale', ['sum', 'count']);
|
||
|
||
2
|
||
|
||
3$users = Pulse::resolveUsers($aggregates->pluck('key'));
|
||
|
||
4
|
||
|
||
5return view('livewire.pulse.top-sellers', [
|
||
|
||
6 'sellers' => $aggregates->map(fn ($aggregate) => (object) [
|
||
|
||
7 'user' => $users->find($aggregate->key),
|
||
|
||
8 'sum' => $aggregate->sum,
|
||
|
||
9 'count' => $aggregate->count,
|
||
|
||
10 ])
|
||
|
||
11]);
|
||
|
||
|
||
$aggregates = $this->aggregate('user_sale', ['sum', 'count']);
|
||
|
||
$users = Pulse::resolveUsers($aggregates->pluck('key'));
|
||
|
||
return view('livewire.pulse.top-sellers', [
|
||
'sellers' => $aggregates->map(fn ($aggregate) => (object) [
|
||
'user' => $users->find($aggregate->key),
|
||
'sum' => $aggregate->sum,
|
||
'count' => $aggregate->count,
|
||
])
|
||
]);
|
||
|
||
The `find` method returns an object containing `name`, `extra`, and `avatar`
|
||
keys, which you may optionally pass directly to the `<x-pulse::user-card>`
|
||
Blade component:
|
||
|
||
|
||
|
||
1<x-pulse::user-card :user="{{ $seller->user }}" :stats="{{ $seller->sum }}" />
|
||
|
||
|
||
<x-pulse::user-card :user="{{ $seller->user }}" :stats="{{ $seller->sum }}" />
|
||
|
||
#### Custom Recorders
|
||
|
||
Package authors may wish to provide recorder classes to allow users to
|
||
configure the capturing of data.
|
||
|
||
Recorders are registered in the `recorders` section of the application's
|
||
`config/pulse.php` configuration file:
|
||
|
||
|
||
|
||
1[
|
||
|
||
2 // ...
|
||
|
||
3 'recorders' => [
|
||
|
||
4 Acme\Recorders\Deployments::class => [
|
||
|
||
5 // ...
|
||
|
||
6 ],
|
||
|
||
7
|
||
|
||
8 // ...
|
||
|
||
9 ],
|
||
|
||
10]
|
||
|
||
|
||
[
|
||
// ...
|
||
'recorders' => [
|
||
Acme\Recorders\Deployments::class => [
|
||
// ...
|
||
],
|
||
|
||
// ...
|
||
],
|
||
]
|
||
|
||
Recorders may listen to events by specifying a `$listen` property. Pulse will
|
||
automatically register the listeners and call the recorders `record` method:
|
||
|
||
|
||
|
||
1<?php
|
||
|
||
2
|
||
|
||
3namespace Acme\Recorders;
|
||
|
||
4
|
||
|
||
5use Acme\Events\Deployment;
|
||
|
||
6use Illuminate\Support\Facades\Config;
|
||
|
||
7use Laravel\Pulse\Facades\Pulse;
|
||
|
||
8
|
||
|
||
9class Deployments
|
||
|
||
10{
|
||
|
||
11 /**
|
||
|
||
12 * The events to listen for.
|
||
|
||
13 *
|
||
|
||
14 * @var array<int, class-string>
|
||
|
||
15 */
|
||
|
||
16 public array $listen = [
|
||
|
||
17 Deployment::class,
|
||
|
||
18 ];
|
||
|
||
19
|
||
|
||
20 /**
|
||
|
||
21 * Record the deployment.
|
||
|
||
22 */
|
||
|
||
23 public function record(Deployment $event): void
|
||
|
||
24 {
|
||
|
||
25 $config = Config::get('pulse.recorders.'.static::class);
|
||
|
||
26
|
||
|
||
27 Pulse::record(
|
||
|
||
28 // ...
|
||
|
||
29 );
|
||
|
||
30 }
|
||
|
||
31}
|
||
|
||
|
||
<?php
|
||
|
||
namespace Acme\Recorders;
|
||
|
||
use Acme\Events\Deployment;
|
||
use Illuminate\Support\Facades\Config;
|
||
use Laravel\Pulse\Facades\Pulse;
|
||
|
||
class Deployments
|
||
{
|
||
/**
|
||
* The events to listen for.
|
||
*
|
||
* @var array<int, class-string>
|
||
*/
|
||
public array $listen = [
|
||
Deployment::class,
|
||
];
|
||
|
||
/**
|
||
* Record the deployment.
|
||
*/
|
||
public function record(Deployment $event): void
|
||
{
|
||
$config = Config::get('pulse.recorders.'.static::class);
|
||
|
||
Pulse::record(
|
||
// ...
|
||
);
|
||
}
|
||
}
|
||
|