简介

Laravel Pulse 能让你一眼掌握应用程序的性能与使用情况。借助 Pulse,你可以排查诸如慢任务和慢端点等性能瓶颈,找到最活跃的用户,还能做更多事情。

若要对单个事件进行深入调试,请查看 Laravel Telescope。

安装

Pulse 的官方存储实现目前需要 MySQL、MariaDB 或 PostgreSQL 数据库。如果你使用的是其他数据库引擎,则需要单独准备一个 MySQL、MariaDB 或 PostgreSQL 数据库来存放 Pulse 数据。

你可以使用 Composer 包管理器安装 Pulse:

composer require laravel/pulse

接下来,你应该使用 vendor:publish Artisan 命令发布 Pulse 的配置文件和迁移文件:

php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"

最后,你应该运行 migrate 命令,以创建存储 Pulse 数据所需的表:

php artisan migrate

一旦运行了 Pulse 的数据库迁移,你就可以通过 /pulse 路由访问 Pulse 仪表盘。

如果你不想将 Pulse 数据存储在应用程序的主数据库中,可以指定一个专用的数据库连接。

配置

Pulse 的许多配置选项可通过环境变量进行控制。若要查看可用选项、注册新的记录器或配置高级选项,你可以发布 config/pulse.php 配置文件:

php artisan vendor:publish --tag=pulse-config

仪表盘

授权

可通过 /pulse 路由访问 Pulse 仪表盘。默认情况下,你只能在 local 环境中访问此仪表盘,因此你需要通过自定义 'viewPulse' 授权门(gate)为生产环境配置授权。你可以在应用程序的 app/Providers/AppServiceProvider.php 文件中完成此操作:

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();
    });

    // ...
}

自定义

可以通过发布仪表盘视图来配置 Pulse 仪表盘卡片与布局。仪表盘视图会被发布到 resources/views/vendor/pulse/dashboard.blade.php:

php artisan vendor:publish --tag=pulse-dashboard

该仪表盘由 Livewire 驱动,允许你在无需重新构建任何 JavaScript 资源的情况下自定义卡片和布局。

在该文件中,<x-pulse> 组件负责渲染仪表盘,并为卡片提供网格布局。如果你希望仪表盘占满整个屏幕宽度,可以为该组件传入 full-width 属性:

<x-pulse full-width>
    ...
</x-pulse>

默认情况下,<x-pulse> 组件会创建 12 列网格,但你可以使用 cols 属性对此进行自定义:

<x-pulse cols="16">
    ...
</x-pulse>

每张卡片都支持 cols 和 rows 属性,用于控制所占空间与位置:

<livewire:pulse.usage cols="4" rows="2" />

大多数卡片还支持 expand 属性,用于显示完整卡片而不是滚动:

<livewire:pulse.slow-queries expand />

解析用户

对于展示用户信息的卡片(如“应用程序使用情况”卡片),Pulse 仅会记录用户的 ID。渲染仪表盘时,Pulse 会从默认的 Authenticatable 模型中解析 name 和 email 字段,并使用 Gravatar 网络服务显示头像。

你可以在应用程序的 App\Providers\AppServiceProvider 类中调用 Pulse::user 方法,自定义这些字段和头像。

user 方法接收一个闭包,该闭包会收到要展示的 Authenticatable 模型,并应返回包含用户 name、extra 和 avatar 信息的数组:

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,
    ]);

    // ...
}

你可以通过实现 Laravel\Pulse\Contracts\ResolvesUsers 契约并将其绑定到 Laravel 的服务容器,完全自定义认证用户的捕获与获取方式。

卡片

服务器

<livewire:pulse.servers /> 卡片会显示所有运行 pulse:check 命令的服务器的系统资源使用情况。请参阅关于服务器记录器的文档,以了解更多有关系统资源报告的信息。

如果你在基础设施中替换了某台服务器,可能会希望在给定时长后停止在 Pulse 仪表盘中展示这台不活跃的服务器。你可以通过 ignore-after 属性来实现,该属性接收一个秒数,表示不活跃服务器应在此秒数后从 Pulse 仪表盘中移除。也可以传入相对时间格式的字符串,例如 1 hour 或 3 days and 1 hour:

<livewire:pulse.servers ignore-after="3 hours" />

应用程序使用情况

<livewire:pulse.usage /> 卡片会展示向你的应用程序发起请求、派发任务以及遇到慢请求的排名前 10 的用户。

如果你想在同一屏幕上同时查看所有使用指标,可以多次引入该卡片并指定 type 属性:

<livewire:pulse.usage type="requests" />
<livewire:pulse.usage type="slow_requests" />
<livewire:pulse.usage type="jobs" />

若要了解如何自定义 Pulse 获取与展示用户信息的方式,请参阅我们关于“解析用户”的文档。

如果你的应用程序接收大量请求或派发大量任务,可能需要开启采样。请参阅用户请求记录器、用户任务记录器和慢任务记录器的文档以了解更多信息。

异常

<livewire:pulse.exceptions /> 卡片会显示应用程序中发生异常的频率和最近情况。默认情况下,异常会根据异常类及其发生位置进行分组。请参阅异常记录器文档以了解更多信息。

队列

<livewire:pulse.queues /> 卡片会显示应用程序中队列的吞吐量,包括排队、处理中、已处理、已释放和失败的任务数量。请参阅队列记录器文档以了解更多信息。

慢请求

<livewire:pulse.slow-requests /> 卡片会显示进入你应用程序且超过配置阈值(默认为 1000ms)的请求。请参阅慢请求记录器文档以了解更多信息。

慢任务

<livewire:pulse.slow-jobs /> 卡片会显示你应用程序中超过配置阈值(默认为 1000ms)的队列任务。请参阅慢任务记录器文档以了解更多信息。

慢查询

<livewire:pulse.slow-queries /> 卡片会显示你应用程序中超过配置阈值(默认为 1000ms)的数据库查询。

默认情况下,慢查询会根据 SQL 查询(不含绑定参数)及其发生位置进行分组,但如果你只想依据 SQL 查询进行分组,也可以选择不采集位置信息。

如果你因超大型 SQL 查询执行语法高亮而遇到渲染性能问题,可以添加 without-highlighting 属性来禁用高亮:

<livewire:pulse.slow-queries without-highlighting />

请参阅慢查询记录器文档以了解更多信息。

慢出站请求

<livewire:pulse.slow-outgoing-requests /> 卡片会显示使用 Laravel 的HTTP 客户端发出的、超过配置阈值(默认为 1000ms)的出站请求。

默认情况下,条目会按完整 URL 分组。不过,你也可以使用正则表达式对相似的出站请求进行归一化或分组。请参阅慢出站请求记录器文档以了解更多信息。

缓存

<livewire:pulse.cache /> 卡片会显示你应用程序的缓存命中率与未命中率统计,包括全局统计以及单个键的统计。

默认情况下,条目将按键分组。不过,您也可以使用正则表达式对相似的键进行标准化或分组。有关详细信息,请参阅缓存交互记录器的文档。

采集条目

大多数 Pulse 记录器会根据 Laravel 派发的框架事件自动采集条目。不过,服务器记录器和一些第三方卡片必须定期轮询信息。若要使用这些卡片,您必须在所有独立的应用服务器上运行 pulse:check 守护进程:

php artisan pulse:check

要让 pulse:check 进程在后台永久运行,您应使用 Supervisor 等进程监控工具,以确保该命令不会停止运行。

由于 pulse:check 命令属于长驻进程,若不重启则无法感知代码库的变更。您应在应用部署过程中调用 pulse:restart 命令,以便优雅地重启该命令:

php artisan pulse:restart

Pulse 使用缓存来存储重启信号,因此在使用此功能前,您应确认应用已正确配置缓存驱动。

记录器

记录器负责从您的应用中采集条目,并将其记录到 Pulse 数据库中。记录器在 Pulse 配置文件的 recorders 部分进行注册与配置。

缓存交互

CacheInteractions 记录器会采集应用中发生的缓存命中与未命中信息,以便在“缓存”卡片中展示。

您可以按需调整采样率和要忽略的键模式。

您还可以配置键分组,将相似的键归并为单个条目。例如,您可能希望从缓存同一类信息的键中移除唯一 ID。分组通过正则表达式对键的相应部分执行“查找并替换”来实现。配置文件中已包含一个示例:

Recorders\CacheInteractions::class => [
    // ...
    'groups' => [
        // '/:\d+/' => ':*',
    ],
],

系统将使用第一个匹配成功的模式。如果没有模式匹配,键将按原样采集。

异常

Exceptions 记录器会采集应用中可报告异常的相关信息,以便在“异常”卡片中展示。

您可以按需调整采样率和要忽略的异常模式。您还可以配置是否采集异常的来源位置。采集到的位置将显示在 Pulse 仪表板中,有助于定位异常来源;不过,如果同一异常在多个位置出现,则会在每个唯一位置分别显示多次。

队列

Queues 记录器会采集应用队列的相关信息,以便在“队列”卡片中展示。

您可以按需调整采样率和要忽略的作业模式。

慢作业

SlowJobs 记录器会采集应用中慢作业的相关信息,以便在“慢作业”卡片中展示。

您可以按需调整慢作业阈值、采样率以及要忽略的作业模式。

某些作业可能比其它作业耗时更长,在这种情况下,您可以为单个作业配置阈值:

Recorders\SlowJobs::class => [
    // ...
    'threshold' => [
        '#^App\\Jobs\\GenerateYearlyReports$#' => 5000,
        'default' => env('PULSE_SLOW_JOBS_THRESHOLD', 1000),
    ],
],

如果没有任何正则表达式模式与作业类名匹配,则将使用 'default' 的值。

慢出站请求

SlowOutgoingRequests 记录器会采集使用 Laravel 的HTTP 客户端发起且超过所配置阈值的出站 HTTP 请求相关信息,以便在“慢出站请求”卡片中展示。

您可以按需调整慢出站请求阈值、采样率以及要忽略的 URL 模式。

某些出站请求可能比其它请求耗时更长,在这种情况下,您可以为单个请求配置阈值:

Recorders\SlowOutgoingRequests::class => [
    // ...
    'threshold' => [
        '#backup.zip$#' => 5000,
        'default' => env('PULSE_SLOW_OUTGOING_REQUESTS_THRESHOLD', 1000),
    ],
],

如果没有任何正则表达式模式与请求的 URL 匹配,则将使用 'default' 的值。

您还可以配置 URL 分组,将相似的 URL 归并为单个条目。例如,您可能希望从 URL 路径中移除唯一 ID,或仅按域名分组。分组通过正则表达式对 URL 的相应部分执行“查找并替换”来实现。配置文件中已包含一些示例:

Recorders\SlowOutgoingRequests::class => [
    // ...
    'groups' => [
        // '#^https://api\.github\.com/repos/.*$#' => 'api.github.com/repos/*',
        // '#^https?://([^/]*).*$#' => '\1',
        // '#/\d+#' => '/*',
    ],
],

系统将使用第一个匹配成功的模式。如果没有模式匹配,URL 将按原样采集。

慢查询

SlowQueries 记录器会采集应用中超过所配置阈值的任何数据库查询,以便在“慢查询”卡片中展示。

您可以按需调整慢查询阈值、采样率以及要忽略的查询模式。您还可以配置是否采集查询位置。采集到的位置将显示在 Pulse 仪表板中,有助于定位查询来源;不过,如果同一查询在多个位置执行,则会在每个唯一位置分别显示多次。

某些查询可能比其它查询耗时更长,在这种情况下,您可以为单个查询配置阈值:

Recorders\SlowQueries::class => [
    // ...
    'threshold' => [
        '#^insert into `yearly_reports`#' => 5000,
        'default' => env('PULSE_SLOW_QUERIES_THRESHOLD', 1000),
    ],
],

如果没有任何正则表达式模式与查询的 SQL 匹配,则将使用 'default' 的值。

慢请求

Requests 记录器会采集发往您应用的请求相关信息,以便在“慢请求”和“应用用量”卡片中展示。

您可以按需调整慢路由阈值、采样率以及要忽略的路径。

某些请求可能比其它请求耗时更长,在这种情况下,您可以为单个请求配置阈值:

Recorders\SlowRequests::class => [
    // ...
    'threshold' => [
        '#^/admin/#' => 5000,
        'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
    ],
],

如果没有任何正则表达式模式与请求的 URL 匹配,则将使用 'default' 的值。

服务器

Servers 记录器会采集支撑您应用的服务器的 CPU、内存及存储使用情况,以便在“服务器”卡片中展示。该记录器要求在您希望监控的每台服务器上运行 pulse:check 命令。

每台上报服务器必须拥有唯一名称。默认情况下,Pulse 将使用 PHP 的 gethostname 函数返回的值。若希望自定义,您可以设置 PULSE_SERVER_NAME 环境变量:

PULSE_SERVER_NAME=load-balancer

Pulse 配置文件还允许您自定义受监控的目录。

用户作业

UserJobs 记录器会采集应用中派发作业的用户相关信息,以便在“应用用量”卡片中展示。

您可以按需调整采样率和要忽略的作业模式。

用户请求

UserRequests 记录器会采集向应用发起请求的用户相关信息,以便在“应用用量”卡片中展示。

您可以按需调整采样率和要忽略的 URL 模式。

过滤

如前所述,许多记录器支持通过配置按值“忽略”传入条目,例如请求的 URL。但有时,可能需要依据其他因素(如当前已认证用户)过滤记录。为实现这一点,您可以将一个闭包传递给 Pulse 的 filter 方法。通常,filter 方法应在应用的 AppServiceProvider 的 boot 方法中调用:

use Illuminate\Support\Facades\Auth;
use Laravel\Pulse\Entry;
use Laravel\Pulse\Facades\Pulse;
use Laravel\Pulse\Value;

/**
 * 初始化任何应用服务。
 */
public function boot(): void
{
    Pulse::filter(function (Entry|Value $entry) {
        return Auth::user()->isNotAdmin();
    });

    // ...
}

性能

Pulse 的设计目标是可以无缝接入现有应用,无需任何额外基础设施。不过,对于高流量应用,有几种方式可以消除 Pulse 对应用性能可能产生的影响。

使用不同的数据库

对于高流量应用,您可能更倾向于为 Pulse 使用专用数据库连接,以避免对应用数据库造成影响。

您可以通过设置 PULSE_DB_CONNECTION 环境变量,来自定义 Pulse 所使用的数据库连接。

PULSE_DB_CONNECTION=pulse

Redis 数据接收

Redis 数据接收需要 Redis 6.2 或更高版本,且需将 phpredis 或 predis 设为应用配置的 Redis 客户端驱动。

默认情况下,Pulse 会在向客户端发送 HTTP 响应或处理完作业后,直接将条目存入已配置的数据库连接;不过,您也可以使用 Pulse 的 Redis 接收驱动,将条目发送到 Redis 流中。可通过配置 PULSE_INGEST_DRIVER 环境变量来启用此功能:

PULSE_INGEST_DRIVER=redis

默认情况下,Pulse 会使用您的默认 Redis 连接,但您可以通过 PULSE_REDIS_CONNECTION 环境变量对此进行自定义:

PULSE_REDIS_CONNECTION=pulse

使用 Redis 接收驱动时,您的 Pulse 安装实例应始终使用与基于 Redis 的队列不同的 Redis 连接(如适用)。

使用 Redis 接收功能时,您需要运行 pulse:work 命令来监控流,并将条目从 Redis 迁移到 Pulse 的数据库表中。

php artisan pulse:work

若要让 pulse:work 进程在后台永久运行,应使用 Supervisor 等进程监控工具,确保 Pulse 工作进程不会停止运行。

由于 pulse:work 命令是长期运行的进程,若不重启,它将无法感知代码库的变更。在应用部署过程中,应通过调用 pulse:restart 命令优雅地重启该命令:

php artisan pulse:restart

Pulse 使用缓存存储重启信号,因此在使用此功能前,应确保应用已正确配置缓存驱动。

采样

默认情况下,Pulse 会捕获应用中发生的所有相关事件。对于高流量应用,这可能会导致仪表盘需要聚合数百万行数据库记录,尤其是在查询较长时间段时。

您可以选择为某些 Pulse 数据记录器启用“采样”。例如,将用户请求记录器的采样率设为 0.1,意味着仅会记录应用约 10% 的请求。在仪表盘中,数值会按比例放大,并添加 ~ 前缀以表明其为近似值。

一般而言,特定指标的条目数量越多,就越能安全地降低采样率,且不会过度牺牲准确性。

数据修剪

一旦存储的条目超出仪表盘显示窗口,Pulse 会自动进行修剪。修剪操作在通过彩票系统摄取数据时执行,该系统可在 Pulse 配置文件中自定义。

处理 Pulse 异常

若在捕获 Pulse 数据时发生异常(如无法连接到存储数据库),Pulse 会静默失败,以避免对应用造成影响。

若希望自定义异常的处理方式,可向 handleExceptionsUsing 方法传入一个闭包:

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(),
    ]);
});

自定义卡片

Pulse 支持您构建自定义卡片,以展示契合应用特定需求的数据。Pulse 基于 Livewire,因此建议在构建首个自定义卡片前,先查阅其文档。

卡片组件

在 Laravel Pulse 中创建自定义卡片,首先需继承基础 Card Livewire 组件,并定义对应的视图:

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');
    }
}

使用 Livewire 的延迟加载功能时,Card 组件会自动提供占位符,且该占位符会遵循传递给组件的 cols 和 rows 属性。

编写 Pulse 卡片对应的视图时,可借助 Pulse 的 Blade 组件,以实现一致的视觉效果与交互体验:

<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>

应将 $cols、$rows、$class 和 $expand 变量传递给各自的 Blade 组件,以便从仪表盘视图自定义卡片布局。您也可在视图中添加 wire:poll.5s="" 属性,使卡片自动更新。

定义好 Livewire 组件和模板后,即可将卡片添加到仪表盘视图中:

<x-pulse>
    ...

    <livewire:pulse.top-sellers cols="4" />
</x-pulse>

若卡片包含在包中,需使用 Livewire::component 方法向 Livewire 注册该组件。

样式

若卡片需要的样式超出了 Pulse 自带的类和组件,可通过以下几种方式为卡片添加自定义 CSS。

Laravel Vite 集成

若自定义卡片位于应用代码库内,且正在使用 Laravel 的 Vite 集成,可更新 vite.config.js 文件,为卡片添加专属 CSS 入口:

laravel({
    input: [
        'resources/css/pulse/top-sellers.css',
        // ...
    ],
}),

随后可在仪表盘视图中使用 @vite Blade 指令,指定卡片的 CSS 入口:

<x-pulse>
    @vite('resources/css/pulse/top-sellers.css')

    ...
</x-pulse>

CSS 文件

对于其他场景(如包含在包中的 Pulse 卡片),可通过在 Livewire 组件中定义返回 CSS 文件路径的 css 方法,指示 Pulse 加载额外的样式表:

class TopSellers extends Card
{
    // ...

    protected function css()
    {
        return __DIR__.'/../../dist/top-sellers.css';
    }
}

当该卡片添加到仪表盘时,Pulse 会自动将此文件内容包裹在 <style> 标签中,因此无需将其发布到 public 目录。

Tailwind CSS

使用 Tailwind CSS 时,应创建专属的 CSS 入口。以下示例排除了 Pulse 已包含的 Tailwind Preflight 基础样式,并通过 CSS 选择器对 Tailwind 进行作用域限定,以避免与 Pulse 的 Tailwind 类产生冲突:

@import "tailwindcss/theme.css";

@custom-variant dark (&:where(.dark, .dark *));
@source "./../../views/livewire/pulse/top-sellers.blade.php";

@theme {
  /* ... */
}

#top-sellers {
  @import "tailwindcss/utilities.css" source(none);
}

还需在卡片视图中添加与入口中 CSS 选择器匹配的 id 或 class 属性:

<x-pulse::card id="top-sellers" :cols="$cols" :rows="$rows" class="$class">
    ...
</x-pulse::card>

数据采集与聚合

自定义卡片可从任意位置获取并展示数据;不过,您也可借助 Pulse 强大且高效的数据记录与聚合系统。

捕获条目

Pulse 支持通过 Pulse::record 方法记录“条目”:

use Laravel\Pulse\Facades\Pulse;

Pulse::record('user_sale', $user->id, $sale->amount)
    ->sum()
    ->count();

传递给 record 方法的第一个参数是您要记录的条目的 type,而第二个参数是决定聚合数据应如何分组的 key。对于大多数聚合方法,您还需要指定要聚合的 value。在上面的示例中,正在聚合的值为 $sale->amount。然后,您可以调用一个或多个聚合方法(例如 sum),以便 Pulse 可以将预聚合的值捕获到“存储桶”中,以便日后高效检索。

可用的聚合方法包括:

  • avg
  • count
  • max
  • min
  • sum

当构建用于捕获当前已认证用户 ID 的卡片包时,您应使用 Pulse::resolveAuthenticatedUserId() 方法,该方法会遵循为应用程序所做的任何用户解析器自定义设置。

检索聚合数据

在扩展 Pulse 的 Card Livewire 组件时,您可以使用 aggregate 方法来检索仪表板中当前查看时间段的聚合数据:

class TopSellers extends Card
{
    public function render()
    {
        return view('livewire.pulse.top-sellers', [
            'topSellers' => $this->aggregate('user_sale', ['sum', 'count'])
        ]);
    }
}

aggregate 方法返回一个 PHP stdClass 对象集合。每个对象将包含之前捕获的 key 属性,以及每个请求的聚合结果对应的键:

@foreach ($topSellers as $seller)
    {{ $seller->key }}
    {{ $seller->sum }}
    {{ $seller->count }}
@endforeach

Pulse 主要会从预聚合的存储桶中检索数据;因此,指定的聚合项必须事先使用 Pulse::record 方法进行捕获。最旧的存储桶通常部分超出所查看的时间段,因此 Pulse 将对最旧的条目进行聚合,以填补空缺并提供整个时间段内的准确数值,而无需在每次轮询请求时对整个时间段进行聚合。

您还可以使用 aggregateTotal 方法检索给定类型的总值。例如,以下方法将检索所有用户销售的总和,而不是按用户进行分组。

$total = $this->aggregateTotal('user_sale', 'sum');

显示用户

当处理以用户 ID 作为键记录的聚合数据时,您可以使用 Pulse::resolveUsers 方法将键解析为用户记录:

$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,
    ])
]);

find 方法返回一个包含 name、extra 和 avatar 键的对象,您可以选择将其直接传递给 <x-pulse::user-card> Blade 组件:

<x-pulse::user-card :user="{{ $seller->user }}" :stats="{{ $seller->sum }}" />

自定义记录器

包的开发者可能希望提供记录器类,以便用户配置数据的捕获。

记录器在应用程序的 config/pulse.php 配置文件的 recorders 部分进行注册:

[
    // ...
    'recorders' => [
        Acme\Recorders\Deployments::class => [
            // ...
        ],

        // ...
    ],
]

记录器可以通过指定 $listen 属性来监听事件。Pulse 会自动注册监听器并调用记录器的 record 方法:

<?php

namespace Acme\Recorders;

use Acme\Events\Deployment;
use Illuminate\Support\Facades\Config;
use Laravel\Pulse\Facades\Pulse;

class Deployments
{
    /**
     * 要监听的事件。
     *
     * @var array<int, class-string>
     */
    public array $listen = [
        Deployment::class,
    ];

    /**
     * 记录部署。
     */
    public function record(Deployment $event): void
    {
        $config = Config::get('pulse.recorders.'.static::class);

        Pulse::record(
            // ...
        );
    }
}