feat: add Laravel Horizon for queue monitoring and management

- Install Horizon package and configure with default settings
- Add HorizonServiceProvider with access gate for super_admin role only
- Integrate Horizon dashboard link into Filament admin panel navigation
- Update composer dependencies including Horizon and related package updates
This commit is contained in:
Jp
2026-02-12 16:29:30 +08:00
parent a8ad07676a
commit a80e9a7b1c
5 changed files with 315 additions and 15 deletions

View File

@@ -3,6 +3,7 @@
namespace App\Providers\Filament; namespace App\Providers\Filament;
use BezhanSalleh\FilamentShield\FilamentShieldPlugin; use BezhanSalleh\FilamentShield\FilamentShieldPlugin;
use Filament\Navigation\NavigationItem;
use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents; use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent; use Filament\Http\Middleware\DispatchServingFilamentEvent;
@@ -39,6 +40,13 @@ class AdminPanelProvider extends PanelProvider
->pages([ ->pages([
Pages\Dashboard::class, Pages\Dashboard::class,
]) ])
->navigationItems([
NavigationItem::make('Horizon')
->url(fn (): string => url(config('horizon.path')))
->icon('heroicon-o-queue-list')
->group('System')
->visible(fn (): bool => auth()->user()?->hasRole('super_admin') ?? false),
])
->databaseNotifications() ->databaseNotifications()
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets') ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([ ->widgets([

View File

@@ -0,0 +1,34 @@
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Gate;
use Laravel\Horizon\Horizon;
use Laravel\Horizon\HorizonApplicationServiceProvider;
class HorizonServiceProvider extends HorizonApplicationServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
parent::boot();
// Horizon::routeSmsNotificationsTo('15556667777');
// Horizon::routeMailNotificationsTo('example@example.com');
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
}
/**
* Register the Horizon gate.
*
* This gate determines who can access Horizon in non-local environments.
*/
protected function gate(): void
{
Gate::define('viewHorizon', function ($user) {
return $user->hasRole('super_admin');
});
}
}

View File

@@ -13,6 +13,7 @@
"bezhansalleh/filament-shield": "^3.2", "bezhansalleh/filament-shield": "^3.2",
"filament/filament": "^3.2", "filament/filament": "^3.2",
"laravel/framework": "^11.9", "laravel/framework": "^11.9",
"laravel/horizon": "^5.44",
"laravel/tinker": "^2.9", "laravel/tinker": "^2.9",
"livewire/livewire": "^3.4", "livewire/livewire": "^3.4",
"livewire/volt": "^1.0", "livewire/volt": "^1.0",

109
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "68d9041cb27c73080bced01f909a4567", "content-hash": "60653f82a7ab603cb6e06b89358e7eec",
"packages": [ "packages": [
{ {
"name": "anourvalar/eloquent-serialize", "name": "anourvalar/eloquent-serialize",
@@ -386,16 +386,16 @@
}, },
{ {
"name": "brick/math", "name": "brick/math",
"version": "0.14.7", "version": "0.14.8",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/brick/math.git", "url": "https://github.com/brick/math.git",
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50" "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/brick/math/zipball/07ff363b16ef8aca9692bba3be9e73fe63f34e50", "url": "https://api.github.com/repos/brick/math/zipball/63422359a44b7f06cae63c3b429b59e8efcc0629",
"reference": "07ff363b16ef8aca9692bba3be9e73fe63f34e50", "reference": "63422359a44b7f06cae63c3b429b59e8efcc0629",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -434,7 +434,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/brick/math/issues", "issues": "https://github.com/brick/math/issues",
"source": "https://github.com/brick/math/tree/0.14.7" "source": "https://github.com/brick/math/tree/0.14.8"
}, },
"funding": [ "funding": [
{ {
@@ -442,7 +442,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2026-02-07T10:57:35+00:00" "time": "2026-02-10T14:33:43+00:00"
}, },
{ {
"name": "carbonphp/carbon-doctrine-types", "name": "carbonphp/carbon-doctrine-types",
@@ -2617,17 +2617,96 @@
"time": "2026-01-20T15:26:20+00:00" "time": "2026-01-20T15:26:20+00:00"
}, },
{ {
"name": "laravel/prompts", "name": "laravel/horizon",
"version": "v0.3.12", "version": "v5.44.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/prompts.git", "url": "https://github.com/laravel/horizon.git",
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8" "reference": "00c21e4e768112cce3f4fe576d75956dfc423de2"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/4861ded9003b7f8a158176a0b7666f74ee761be8", "url": "https://api.github.com/repos/laravel/horizon/zipball/00c21e4e768112cce3f4fe576d75956dfc423de2",
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8", "reference": "00c21e4e768112cce3f4fe576d75956dfc423de2",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-pcntl": "*",
"ext-posix": "*",
"illuminate/contracts": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/queue": "^9.21|^10.0|^11.0|^12.0|^13.0",
"illuminate/support": "^9.21|^10.0|^11.0|^12.0|^13.0",
"nesbot/carbon": "^2.17|^3.0",
"php": "^8.0",
"ramsey/uuid": "^4.0",
"symfony/console": "^6.0|^7.0|^8.0",
"symfony/error-handler": "^6.0|^7.0|^8.0",
"symfony/polyfill-php83": "^1.28",
"symfony/process": "^6.0|^7.0|^8.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.55|^8.36|^9.15|^10.8|^11.0",
"phpstan/phpstan": "^1.10|^2.0",
"predis/predis": "^1.1|^2.0|^3.0"
},
"suggest": {
"ext-redis": "Required to use the Redis PHP driver.",
"predis/predis": "Required when not using the Redis PHP driver (^1.1|^2.0|^3.0)."
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"Horizon": "Laravel\\Horizon\\Horizon"
},
"providers": [
"Laravel\\Horizon\\HorizonServiceProvider"
]
},
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\Horizon\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"description": "Dashboard and code-driven configuration for Laravel queues.",
"keywords": [
"laravel",
"queue"
],
"support": {
"issues": "https://github.com/laravel/horizon/issues",
"source": "https://github.com/laravel/horizon/tree/v5.44.0"
},
"time": "2026-02-10T18:18:08+00:00"
},
{
"name": "laravel/prompts",
"version": "v0.3.13",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "ed8c466571b37e977532fb2fd3c272c784d7050d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/ed8c466571b37e977532fb2fd3c272c784d7050d",
"reference": "ed8c466571b37e977532fb2fd3c272c784d7050d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2671,9 +2750,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.", "description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": { "support": {
"issues": "https://github.com/laravel/prompts/issues", "issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.3.12" "source": "https://github.com/laravel/prompts/tree/v0.3.13"
}, },
"time": "2026-02-03T06:57:26+00:00" "time": "2026-02-06T12:17:10+00:00"
}, },
{ {
"name": "laravel/serializable-closure", "name": "laravel/serializable-closure",

178
config/horizon.php Normal file
View File

@@ -0,0 +1,178 @@
<?php
use Illuminate\Support\Str;
return [
/*
|--------------------------------------------------------------------------
| Horizon Domain
|--------------------------------------------------------------------------
|
| This value is the prefix that will be used by Horizon to generate its
| routes. You are free to change this value to anything you like,
| such as "admin/horizon" or anything else you'd like it to be.
|
*/
'domain' => env('HORIZON_DOMAIN'),
/*
|--------------------------------------------------------------------------
| Horizon Path
|--------------------------------------------------------------------------
|
| This is the URI path where Horizon will be accessible from. Feel free
| to change this path to anything you like. Note that the path will be
| applied after the "domain" value defined above.
|
*/
'path' => env('HORIZON_PATH', 'horizon'),
/*
|--------------------------------------------------------------------------
| Horizon Redis Connection
|--------------------------------------------------------------------------
|
| This is the name of the Redis connection where Horizon will store the
| information about your jobs. This connection should be defined in
| your "database" configuration file.
|
*/
'use' => 'default',
/*
|--------------------------------------------------------------------------
| Horizon Redis Prefix
|--------------------------------------------------------------------------
|
| This prefix will be used when storing all Horizon data in Redis. You
| may modify the prefix if you are running multiple Horizon instances
| on the same server to avoid any collision between instances.
|
*/
'prefix' => env(
'HORIZON_PREFIX',
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
),
/*
|--------------------------------------------------------------------------
| Horizon Route Middleware
|--------------------------------------------------------------------------
|
| These middleware will get attached onto each Horizon route, giving you
| the chance to add your own middleware to this list or change any of
| the existing middleware. Or, you can simply stick with this list.
|
*/
'middleware' => ['web', 'auth'],
/*
|--------------------------------------------------------------------------
| Queue Wait Time Thresholds
|--------------------------------------------------------------------------
|
| This option allows you to configure when the queue "wait time" is
| considered "long", helping you identify which queues are being
| backed up and need more workers to handle the incoming load.
|
*/
'waits' => [
'redis:default' => 60,
],
/*
|--------------------------------------------------------------------------
| Job Trimming Times
|--------------------------------------------------------------------------
|
| These options determine how many minutes Horizon will keep different
| types of jobs in its database. This helps keep your database small
| and fast, while still giving you some history for debugging.
|
*/
'trim' => [
'recent' => 60,
'recent_failed' => 10080,
'failed' => 10080,
'monitored' => 10080,
],
/*
|--------------------------------------------------------------------------
| Metrics Configuration
|--------------------------------------------------------------------------
|
| Here you may configure how many minutes Horizon will keep metrics.
| These metrics include things like throughput and average wait time
| for each of your queues, giving you insight into your performance.
|
*/
'metrics' => [
'trim_snapshots' => [
'job' => 24,
'queue' => 24,
],
],
/*
|--------------------------------------------------------------------------
| Fast Termination
|--------------------------------------------------------------------------
|
| When this option is enabled, Horizon's "terminate" command will not
| wait for all jobs to finish before terminating the workers. This
| can be useful when you need to quickly restart your workers.
|
*/
'fast_termination' => false,
/*
|--------------------------------------------------------------------------
| Memory Limit (MB)
|--------------------------------------------------------------------------
|
| This value describes the maximum amount of memory the Horizon master
| process may consume before it is terminated and restarted. This
| helps prevent any memory leaks from affecting the entire server.
|
*/
'memory_limit' => 64,
/*
|--------------------------------------------------------------------------
| Queue Worker Configuration
|--------------------------------------------------------------------------
|
| Here you may define the queue worker settings used by your application
| in all environments. These settings determine how many workers will
| be used for each environment's respective queues.
|
*/
'environments' => [
'production' => [
'supervisor-1' => [
'maxProcesses' => 10,
'balanceMaxShift' => 1,
'balanceCooldown' => 3,
],
],
'local' => [
'supervisor-1' => [
'maxProcesses' => 3,
],
],
],
];