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仪表板。

NOTE

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

配置

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

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

仪表板

授权

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

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>

默认情况下,<x-pulse>组件将创建一个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 />

解析用户

对于显示有关您的用户的信息的卡片,例如应用程序使用情况卡片,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,
    ]);

    // ...
}

NOTE

您可以完全自定义如何捕获和检索经过身份验证的用户,方法是实现Laravel\Pulse\Contracts\ResolvesUsers契约并在Laravel的服务容器中绑定它。

卡片

服务器

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

如果您在基础设施中替换了服务器,您可能希望在Pulse仪表板上停止显示非活动服务器一段时间后。您可以使用ignore-after属性来实现此目的,该属性接受在非活动服务器从Pulse仪表板中删除之前的秒数。或者,您可以提供格式化的相对时间字符串,例如1小时3天1小时:

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检索和显示用户信息的方式,请参阅我们有关解析用户的文档。

NOTE

如果您的应用程序接收到大量请求或分派了大量作业,您可能希望启用采样。有关更多信息,请参阅用户请求记录器用户作业记录器慢作业记录器文档。

异常

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

队列

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

慢请求

<livewire:pulse.slow-requests />卡片显示超过配置阈值的传入请求,默认为1000毫秒。有关更多信息,请参阅慢请求记录器文档。

慢作业

<livewire:pulse.slow-jobs />卡片显示超过配置阈值的应用程序中的排队作业,默认为1000毫秒。有关更多信息,请参阅慢作业记录器文档。

慢查询

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

默认情况下,慢查询根据SQL查询(不包括绑定)和发生位置进行分组,但您可以选择不捕获位置,以仅根据SQL查询进行分组。

如果由于极大的SQL查询接收语法高亮显示而遇到渲染性能问题,您可以通过添加without-highlighting属性来禁用高亮显示:

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

有关更多信息,请参阅慢查询记录器文档。

慢外发请求

<livewire:pulse.slow-outgoing-requests />卡片显示使用Laravel的HTTP客户端发出的超过配置阈值的外发请求,默认为1000毫秒。

默认情况下,条目将按完整URL分组。但是,您可能希望使用正则表达式对类似的外发请求进行归一化或分组。有关更多信息,请参阅慢外发请求记录器文档。

缓存

<livewire:pulse.cache />卡片显示应用程序的缓存命中和未命中统计信息,包括全局和单个键的统计信息。

默认情况下,条目将按键分组。但是,您可能希望使用正则表达式对类似的键进行归一化或分组。有关更多信息,请参阅缓存交互记录器文档。

捕获条目

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

php
php artisan pulse:check

NOTE

为了使pulse:check进程永久在后台运行,您应该使用Supervisor等进程监视器,以确保该命令不会停止运行。

由于pulse:check命令是一个长期运行的进程,它不会在不重新启动的情况下看到代码库的更改。您应该在应用程序部署过程中优雅地重新启动该命令,方法是调用pulse:restart命令:

sh
php artisan pulse:restart

NOTE

Pulse使用缓存存储重启信号,因此在使用此功能之前,您应该验证为应用程序配置了缓存驱动程序。

记录器

记录器负责从应用程序中捕获条目,以便在Pulse数据库中记录。记录器在Pulse配置文件recorders部分中注册和配置。

缓存交互

CacheInteractions记录器捕获有关应用程序中发生的缓存命中和未命中的信息,以显示在缓存卡片上。

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

您还可以配置键分组,以便将相同类型信息的缓存的相似键分组为单个条目。例如,您可能希望从缓存相同类型信息的唯一ID中删除键。组使用正则表达式进行配置,以"查找和替换"键的一部分。配置文件中包含了一个示例:

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

将使用第一个匹配的模式。如果没有模式匹配,则将按原样捕获键。

异常

Exceptions记录器捕获有关应用程序中发生的可报告异常的信息,以显示在异常卡片上。

您可以选择调整采样率和忽略的异常模式。您还可以配置是否捕获异常发生的位置。捕获的位置将显示在Pulse仪表板上,可以帮助跟踪异常的来源;但是,如果同一异常在多个位置发生,它将为每个唯一位置出现多次。

队列

Queues记录器捕获有关应用程序队列的信息,以显示在队列上。

您可以选择调整采样率和忽略的作业模式。

慢作业

SlowJobs记录器捕获有关应用程序中发生的慢作业的信息,以显示在慢作业卡片上。

您可以选择调整慢作业阈值、采样率和忽略的作业模式。

您可能有一些作业预期需要比其他作业更长的时间。在这些情况下,您可以为每个作业配置特定阈值:

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

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

慢外发请求

SlowOutgoingRequests记录器捕获使用Laravel的HTTP客户端发出的超过配置阈值的外发HTTP请求的信息,以显示在慢外发请求卡片上。

您可以选择调整慢外发请求阈值、采样率和忽略的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记录器捕获应用程序中超过配置阈值的任何数据库查询,以显示在慢查询卡片上。

您可以选择调整慢查询阈值、采样率和忽略的查询模式。您还可以配置是否捕获查询位置。捕获的位置将显示在Pulse仪表板上,可以帮助跟踪查询的来源;但是,如果同一查询在多个位置发生,它将为每个唯一位置出现多次。

您可能有一些查询预期需要比其他查询更长的时间。在这些情况下,您可以为每个查询配置特定阈值:

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

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

慢请求

Requests记录器捕获有关发送到应用程序的请求的信息,以显示在慢请求应用程序使用情况卡片上。

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

您可能有一些请求预期需要比其他请求更长的时间。在这些情况下,您可以为每个请求配置特定阈值:

php
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环境变量:

php
PULSE_SERVER_NAME=load-balancer

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

用户作业

UserJobs记录器捕获有关应用程序中分派作业的用户的信息,以显示在应用程序使用情况卡片上。

您可以选择调整采样率和忽略的作业模式。

用户请求

UserRequests记录器捕获有关发送到应用程序的请求的用户的信息,以显示在应用程序使用情况卡片上。

您可以选择调整采样率和忽略的作业模式。

过滤

正如我们所见,许多记录器都提供了一种配置方式,可以根据其值"忽略"传入的条目,例如请求的URL。但是,有时可能需要根据其他因素过滤掉记录,例如当前经过身份验证的用户。要过滤掉这些记录,您可以将闭包传递给Pulse的filter方法。通常,filter方法应该在应用程序的AppServiceProviderboot方法中调用:

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使用的数据库连接

php
PULSE_DB_CONNECTION=pulse

Redis摄取

WARNING

Redis摄取需要Redis 6.2或更高版本,以及phpredispredis作为应用程序配置的Redis客户端驱动程序。

默认情况下,Pulse将在HTTP响应发送到客户端或作业处理完成后直接将条目存储到配置的数据库连接中;但是,您可以使用Pulse的Redis摄取驱动程序将条目发送到Redis流中。这可以通过配置PULSE_INGEST_DRIVER环境变量来启用:

PULSE_INGEST_DRIVER=redis

Pulse将默认使用您的默认Redis连接,但您可以通过PULSE_REDIS_CONNECTION环境变量自定义此设置:

PULSE_REDIS_CONNECTION=pulse

使用Redis摄取时,您需要运行pulse:work命令来监视流并将条目从Redis移动到Pulse的数据库表中。

php
php artisan pulse:work

NOTE

为了使pulse:work进程永久在后台运行,您应该使用Supervisor等进程监视器,以确保Pulse工作进程不会停止运行。

由于pulse:work命令是一个长期运行的进程,它不会在不重新启动的情况下看到代码库的更改。您应该在应用程序部署过程中优雅地重新启动该命令,方法是调用pulse:restart命令:

sh
php artisan pulse:restart

NOTE

Pulse使用缓存存储重启信号,因此在使用此功能之前,您应该验证为应用程序配置了缓存驱动程序。

采样

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

您可以选择在某些Pulse数据记录器上启用"采样"。例如,在User Requests记录器上将采样率设置为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>

NOTE

如果您的卡片位于包中,您将需要使用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卡片包含在包中,您可以指示Pulse加载其他样式表,方法是在Livewire组件上定义一个css方法,该方法返回CSS文件的文件路径:

php
class TopSellers extends Card
{
    // ...

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

当此卡片包含在仪表板上时,Pulse将自动将此文件的内容包含在<style>标签中,因此无需将其发布到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;

您还需要在卡片的视图中包含一个idclass属性,该属性与传递给Tailwind的重要选择器策略的选择器匹配:

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可以捕获预聚合的值到"桶"中,以便于高效检索。

可用的聚合方法有:

  • avg
  • count
  • max
  • min
  • sum

NOTE

当构建一个记录当前经过身份验证的用户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 属性,以及每个请求的聚合的 key:

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

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

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

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 }}" />

自定义记录器

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

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

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

        // ...
    ],
]

记录器可以通过指定 $listen 属性来侦听事件。Pulse 会自动注册监听器并调用 recorders 的 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(
            // ...
        );
    }
}