Skip to content
全部文档

Laravel Pulse

简介

Laravel Pulse 提供应用性能和使用情况的一览式洞察。借助 Pulse,你可以追踪慢任务、慢端点等瓶颈,找出最活跃的用户等。

若要深入调试单个事件,请参阅 Laravel Telescope

安装

WARNING

Pulse 的一方可存储实现目前需要 MySQL、MariaDB 或 PostgreSQL 数据库。若使用其他数据库引擎,需要单独的 MySQL、MariaDB 或 PostgreSQL 数据库存储 Pulse 数据。

可通过 Composer 安装 Pulse:

sh
composer require laravel/pulse

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

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

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

shell
php artisan migrate

运行 Pulse 数据库迁移后,可通过 /pulse 路由访问 Pulse 仪表盘。

INFO

若不想在主数据库中存储 Pulse 数据,可指定专用数据库连接

配置

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

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

仪表盘

授权

Pulse 仪表盘可通过 /pulse 路由访问。默认情况下,仅在 local 环境可访问,因此需通过自定义 'viewPulse' 授权 Gate 为生产环境配置授权。可在 app/Providers/AppServiceProvider.php 中完成:

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

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

仪表盘由 Livewire 驱动,无需重新构建 JavaScript 资源即可自定义卡片和布局。

在此文件中,<x-pulse> 组件负责渲染仪表盘并为卡片提供网格布局。若希望仪表盘占满屏幕宽度,可向组件提供 full-width 属性:

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

默认情况下,&lt;x-pulse&gt; 组件创建 12 列网格,但可使用 cols 属性自定义:

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

每个卡片接受 colsrows 属性以控制空间和位置:

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

大多数卡片还接受 expand 属性,以显示完整卡片而非滚动:

blade
<livewire:pulse.slow-queries expand />

解析用户

对于显示用户信息的卡片(如 Application Usage 卡片),Pulse 仅记录用户 ID。渲染仪表盘时,Pulse 会从默认 Authenticatable 模型解析 nameemail 字段,并使用 Gravatar 服务显示头像。

可在 App\Providers\AppServiceProvider 中调用 Pulse::user 方法自定义字段和头像。

user 方法接受闭包,接收要显示的 Authenticatable 模型,并应返回包含 nameextraavatar 信息的数组:

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

    // ...
}

INFO

可通过实现 Laravel\Pulse\Contracts\ResolvesUsers 契约并在 Laravel 服务容器 中绑定,完全自定义已认证用户的捕获和检索方式。

卡片

服务器

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

若在基础设施中更换服务器,可能希望在给定时间后停止在 Pulse 仪表盘中显示不活跃的服务器。可使用 ignore-after 属性,接受不活跃服务器应从 Pulse 仪表盘移除的秒数。也可提供相对时间格式字符串,如 1 hour3 days and 1 hour

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

应用使用情况

<livewire:pulse.usage /> 卡片显示向应用发起请求、分派任务和遇到慢请求的前 10 名用户。

若希望同时在屏幕上查看所有使用指标,可多次包含该卡片并指定 type 属性:

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

要了解如何自定义 Pulse 检索和显示用户信息,请参阅解析用户文档。

INFO

若应用接收大量请求或分派大量任务,可考虑启用采样。更多信息请参阅 user requests recorderuser jobs recorderslow jobs recorder 文档。

异常

<livewire:pulse.exceptions /> 卡片显示应用中异常的发生频率和最近情况。默认情况下,异常按异常类和发生位置分组。更多信息请参阅 exceptions recorder 文档。

队列

<livewire:pulse.queues /> 卡片显示应用中队列的吞吐量,包括已入队、处理中、已处理、已释放和失败的任务数量。更多信息请参阅 queues recorder 文档。

慢请求

<livewire:pulse.slow-requests /> 卡片显示超过配置阈值(默认 1000ms)的传入请求。更多信息请参阅 slow requests recorder 文档。

慢任务

<livewire:pulse.slow-jobs /> 卡片显示超过配置阈值(默认 1000ms)的队列任务。更多信息请参阅 slow jobs recorder 文档。

慢查询

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

默认情况下,慢查询按 SQL 查询(不含绑定)和发生位置分组,但若希望仅按 SQL 查询分组,可选择不捕获位置。

若因极大 SQL 查询的语法高亮导致渲染性能问题,可添加 without-highlighting 属性禁用高亮:

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

更多信息请参阅 slow queries recorder 文档。

慢出站请求

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

默认情况下,条目按完整 URL 分组。但可使用正则表达式规范化或分组相似的出站请求。更多信息请参阅 slow outgoing requests recorder 文档。

缓存

<livewire:pulse.cache /> 卡片显示应用的全局和单个键的缓存命中与未命中统计。

默认情况下,条目按键分组。但可使用正则表达式规范化或分组相似的键。更多信息请参阅 cache interactions recorder 文档。

捕获条目

大多数 Pulse recorder 会根据 Laravel 分派的事件自动捕获条目。但 servers recorder 和部分第三方卡片需定期轮询信息。要使用这些卡片,必须在所有应用服务器上运行 pulse:check 守护进程:

php
php artisan pulse:check

INFO

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

pulse:check 是长时间运行的进程,不重启不会看到代码库变更。部署过程中应调用 pulse:restart 命令优雅重启:

sh
php artisan pulse:restart

INFO

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

Recorder

Recorder 负责从应用捕获条目并记录到 Pulse 数据库。Recorder 在 Pulse 配置文件recorders 部分注册和配置。

缓存交互

CacheInteractions recorder 捕获应用中发生的缓存 命中和未命中信息,用于在 Cache 卡片上显示。

可选择调整采样率和忽略的键模式。

还可配置键分组,将相似键合并为单个条目。例如,可从缓存相同类型信息的键中移除唯一 ID。分组使用正则表达式对键的部分进行「查找和替换」。配置文件中包含示例:

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

将使用第一个匹配的模式。若无模式匹配,则按键原样捕获。

异常

Exceptions recorder 捕获应用中可报告异常的信息,用于在 Exceptions 卡片上显示。

可选择调整采样率和忽略的异常模式。还可配置是否捕获异常来源位置。捕获的位置会显示在 Pulse 仪表盘上,有助于追踪异常来源;但若同一异常在多个位置发生,则每个唯一位置都会单独显示。

队列

Queues recorder 捕获应用队列信息,用于在 Queues 卡片上显示。

可选择调整采样率和忽略的任务模式。

慢任务

SlowJobs recorder 捕获应用中慢任务的信息,用于在 Slow Jobs 卡片上显示。

可选择调整慢任务阈值、采样率和忽略的任务模式。

某些任务可能比其他任务耗时更长。此时可配置每个任务的阈值:

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

若无正则表达式模式匹配任务的类名,则使用 'default' 值。

慢出站请求

SlowOutgoingRequests recorder 捕获使用 Laravel HTTP 客户端 发出且超过配置阈值的出站 HTTP 请求信息,用于在 Slow Outgoing Requests 卡片上显示。

可选择调整慢出站请求阈值、采样率和忽略的 URL 模式。

某些出站请求可能比其他请求耗时更长。此时可配置每个请求的阈值:

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

若无正则表达式模式匹配请求的 URL,则使用 'default' 值。

还可配置 URL 分组,将相似 URL 合并为单个条目。例如,可从 URL 路径中移除唯一 ID,或仅按域名分组。分组使用正则表达式对 URL 的部分进行「查找和替换」。配置文件中包含一些示例:

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

将使用第一个匹配的模式。若无模式匹配,则按 URL 原样捕获。

慢查询

SlowQueries recorder 捕获应用中超过配置阈值的任何数据库查询,用于在 Slow Queries 卡片上显示。

可选择调整慢查询阈值、采样率和忽略的查询模式。还可配置是否捕获查询位置。捕获的位置会显示在 Pulse 仪表盘上,有助于追踪查询来源;但若同一查询在多个位置执行,则每个唯一位置都会单独显示。

某些查询可能比其他查询耗时更长。此时可配置每个查询的阈值:

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

若无正则表达式模式匹配查询的 SQL,则使用 'default' 值。

慢请求

Requests recorder 捕获发往应用的请求信息,用于在 Slow RequestsApplication Usage 卡片上显示。

可选择调整慢路由阈值、采样率和忽略的路径。

某些请求可能比其他请求耗时更长。此时可配置每个请求的阈值:

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

若无正则表达式模式匹配请求的 URL,则使用 'default' 值。

服务器

Servers recorder 捕获支撑应用的服务器的 CPU、内存和存储使用情况,用于在 Servers 卡片上显示。此 recorder 需要在要监控的每台服务器上运行 pulse:check 命令

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

ini
PULSE_SERVER_NAME=load-balancer

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

用户任务

UserJobs recorder 捕获应用中分派任务的用户信息,用于在 Application Usage 卡片上显示。

可选择调整采样率和忽略的任务模式。

用户请求

UserRequests recorder 捕获向应用发起请求的用户信息,用于在 Application Usage 卡片上显示。

可选择调整采样率和忽略的 URL 模式。

过滤

如前所述,许多 recorder 可通过配置根据其值(如请求 URL)「忽略」传入条目。但有时根据其他因素(如当前已认证用户)过滤记录也很有用。要过滤这些记录,可向 Pulse 的 filter 方法传递闭包。通常应在 AppServiceProviderboot 方法中调用 filter 方法:

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

    // ...
}

性能

Pulse 设计为可直接接入现有应用,无需额外基础设施。但对于高流量应用,有几种方式可消除 Pulse 对应用性能的影响。

使用不同数据库

对于高流量应用,你可能希望为 Pulse 使用专用数据库连接,以避免影响应用数据库。

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

ini
PULSE_DB_CONNECTION=pulse

Redis 摄取

WARNING

Redis 摄取需要 Redis 6.2 或更高版本,且应用配置的 Redis 客户端驱动为 phpredispredis

默认情况下,Pulse 在 HTTP 响应发送给客户端或任务处理完成后,直接将条目存储到配置的数据库连接;但也可使用 Pulse 的 Redis 摄取驱动将条目发送到 Redis stream。通过配置 PULSE_INGEST_DRIVER 环境变量启用:

text
PULSE_INGEST_DRIVER=redis

Pulse 默认使用默认 Redis 连接,但可通过 PULSE_REDIS_CONNECTION 环境变量自定义:

text
PULSE_REDIS_CONNECTION=pulse

使用 Redis 摄取时,需运行 pulse:work 命令监控 stream 并将条目从 Redis 移入 Pulse 数据库表。

php
php artisan pulse:work

INFO

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

pulse:work 是长时间运行的进程,不重启不会看到代码库变更。部署过程中应调用 pulse:restart 命令优雅重启:

sh
php artisan pulse:restart

INFO

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

采样

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

也可选择在特定 Pulse 数据 recorder 上启用「采样」。例如,在 User Requests recorder 上将采样率设为 0.1 意味着仅记录约 10% 的请求。仪表盘中,值会被放大并以 ~ 为前缀,表示为近似值。

通常,某指标的条目越多,就越可以安全地降低采样率而不牺牲太多准确性。

修剪

Pulse 会在存储条目超出仪表盘窗口后自动修剪。修剪在摄取数据时通过抽奖系统发生,可在 Pulse 配置文件 中自定义。

处理 Pulse 异常

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

若要自定义这些异常的处理方式,可向 handleExceptionsUsing 方法提供闭包:

php
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 组件并定义对应视图:

php
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 组件会自动提供占位符,尊重传递给组件的 colsrows 属性。

编写 Pulse 卡片对应视图时,可利用 Pulse 的 Blade 组件保持一致的观感:

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 组件和模板后,可在仪表盘视图中包含该卡片:

blade
<x-pulse>
    ...

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

INFO

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

样式

若卡片需要 Pulse 包含的类和组件之外的额外样式,有几种方式为卡片包含自定义 CSS。

Laravel Vite 集成

若自定义卡片位于应用代码库中且使用 Laravel Vite 集成,可更新 vite.config.js 为卡片包含专用 CSS 入口点:

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

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

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

    ...
</x-pulse>

CSS 文件

对于其他用例(包括包内的 Pulse 卡片),可在 Livewire 组件上定义返回 CSS 文件路径的 css 方法,指示 Pulse 加载额外样式表:

php
class TopSellers extends Card
{
    // ...

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

当此卡片包含在仪表盘上时,Pulse 会自动在 &lt;style&gt; 标签中包含此文件内容,无需发布到 public 目录。

Tailwind CSS

使用 Tailwind CSS 时,应创建专用的 Tailwind 配置文件,以避免加载不必要的 CSS,或与 Pulse 的 Tailwind 类发生冲突:

js
export default {
    darkMode: 'class',
    important: '#top-sellers',
    content: [
        './resources/views/livewire/pulse/top-sellers.blade.php',
    ],
    corePlugins: {
        preflight: false,
    },
};

然后可在 CSS 入口点中指定该配置文件:

css
@config "../../tailwind.top-sellers.config.js";
@tailwind base;
@tailwind components;
@tailwind utilities;

还需在卡片视图中包含与传入 Tailwind important 选择器策略 的选择器相匹配的 idclass 属性:

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

数据捕获与聚合

自定义卡片可从任意位置获取和显示数据;但也可利用 Pulse 强大高效的数据记录和聚合系统。

捕获条目

Pulse 允许使用 Pulse::record 方法记录「条目」:

php
use Laravel\Pulse\Facades\Pulse;

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

record 方法的第一个参数是所记录条目的 type,第二个参数是确定聚合数据如何分组的 key。对于大多数聚合方法,还需指定要聚合的 value。上述示例中,聚合的值是 $sale->amount。然后可调用一个或多个聚合方法(如 sum),以便 Pulse 将预聚合值捕获到「bucket」中,便于后续高效检索。

可用的聚合方法有:

  • avg
  • count
  • max
  • min
  • sum

INFO

构建捕获当前已认证用户 ID 的卡片包时,应使用 Pulse::resolveAuthenticatedUserId() 方法,它会尊重对应用所做的任何用户解析器自定义

检索聚合数据

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

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

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

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

Pulse 主要从预聚合 bucket 检索数据;因此,指定的聚合必须事先使用 Pulse::record 方法捕获。最旧的 bucket 通常会部分超出时段,因此 Pulse 会聚合最旧的条目以填补缺口,给出整个时段的准确值,而无需在每次轮询请求时聚合整个时段。

也可使用 aggregateTotal 方法检索给定类型的总值。例如,以下方法会检索所有用户销售的总值,而非按用户分组。

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

显示用户

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

php
$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 方法返回包含 nameextraavatar 键的对象,可直接传递给 <x-pulse::user-card> Blade 组件:

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

自定义 Recorder

包作者可能希望提供 recorder 类,让用户配置数据捕获。

Recorder 在应用 config/pulse.php 配置文件的 recorders 部分注册:

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

        // ...
    ],
]

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

php
<?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(
            // ...
        );
    }
}