From 7190f4ae2eda9287d25cd05491d4ac41cf54325f Mon Sep 17 00:00:00 2001 From: Matheus Carvalho <41878558+matheus-carvalho@users.noreply.github.com> Date: Thu, 9 May 2024 14:26:48 -0300 Subject: [PATCH] [1.x] Allow thresholds of slow queries, jobs and requests to be customised by regex pattern (#340) * allow thresholds of slow queries, jobs and requests to be customised by regex pattern * Fix lint and tests * Prevent error when Livewire payload contains no components * Improvements customised thresholds * Format slow requests * Revert default config * slow requests UI * format slow jobs recorder * format slow jobs config * format slow jobs ui * formatting * formatting * format slow outgoing config * format slow outgoing recorder * format slow outgoing ui * format slow queries config * format slow queries recorder * Format slow queries threshold * Format usage UI * use @js directive * Remove comment * Only resolve value once * Fix CI * Fix tests * Formatting * Formatting --------- Co-authored-by: Matheus Carvalho Co-authored-by: Jess Archer Co-authored-by: Tim MacDonald --- resources/views/livewire/cache.blade.php | 2 +- resources/views/livewire/slow-jobs.blade.php | 7 ++- .../livewire/slow-outgoing-requests.blade.php | 9 +++- .../views/livewire/slow-queries.blade.php | 7 ++- .../views/livewire/slow-requests.blade.php | 7 ++- resources/views/livewire/usage.blade.php | 10 +++- src/Livewire/SlowJobs.php | 4 ++ src/Livewire/SlowOutgoingRequests.php | 4 ++ src/Livewire/SlowQueries.php | 4 ++ src/Livewire/SlowRequests.php | 4 ++ src/Recorders/Concerns/LivewireRoutes.php | 4 +- src/Recorders/Concerns/Thresholds.php | 27 +++++++++- src/Recorders/SlowJobs.php | 4 +- src/Recorders/SlowOutgoingRequests.php | 4 +- src/Recorders/SlowQueries.php | 4 +- src/Recorders/SlowRequests.php | 11 ++--- tests/Feature/Livewire/SlowJobsTest.php | 4 +- .../Livewire/SlowOutgoingRequestsTest.php | 4 +- tests/Feature/Livewire/SlowQueriesTest.php | 4 +- tests/Feature/Livewire/SlowRequestsTest.php | 4 +- tests/Feature/Recorders/SlowJobsTest.php | 43 +++++++++++++++- .../Recorders/SlowOutgoingRequestsTest.php | 49 +++++++++++++++++++ tests/Feature/Recorders/SlowQueriesTest.php | 39 +++++++++++++++ tests/Feature/Recorders/SlowRequestsTest.php | 48 ++++++++++++++++++ 24 files changed, 275 insertions(+), 32 deletions(-) diff --git a/resources/views/livewire/cache.blade.php b/resources/views/livewire/cache.blade.php index b5dfdb4f..ce5641e5 100644 --- a/resources/views/livewire/cache.blade.php +++ b/resources/views/livewire/cache.blade.php @@ -20,7 +20,7 @@ Str::plural('group', $count) ); @endphp - diff --git a/resources/views/livewire/slow-jobs.blade.php b/resources/views/livewire/slow-jobs.blade.php index a84cb65c..6759db3e 100644 --- a/resources/views/livewire/slow-jobs.blade.php +++ b/resources/views/livewire/slow-jobs.blade.php @@ -2,7 +2,7 @@ @@ -45,6 +45,11 @@ {{ $job->job }} + @if (is_array($config['threshold'])) +

+ {{ $job->threshold }}ms threshold +

+ @endif @if ($config['sample_rate'] < 1) diff --git a/resources/views/livewire/slow-outgoing-requests.blade.php b/resources/views/livewire/slow-outgoing-requests.blade.php index f2d737e9..6196e326 100644 --- a/resources/views/livewire/slow-outgoing-requests.blade.php +++ b/resources/views/livewire/slow-outgoing-requests.blade.php @@ -5,7 +5,7 @@ @@ -20,7 +20,7 @@ Str::plural('group', $count) ); @endphp - @@ -71,6 +71,11 @@ {{ $request->uri }} + @if (is_array($config['threshold'])) +

+ {{ $request->threshold }}ms threshold +

+ @endif
@if ($config['sample_rate'] < 1) diff --git a/resources/views/livewire/slow-queries.blade.php b/resources/views/livewire/slow-queries.blade.php index 0b3672b6..55abeb3e 100644 --- a/resources/views/livewire/slow-queries.blade.php +++ b/resources/views/livewire/slow-queries.blade.php @@ -20,7 +20,7 @@ @@ -68,6 +68,11 @@ {{ $query->location }}

@endif + @if (is_array($config['threshold'])) +

+ {{ $query->threshold }}ms threshold +

+ @endif
diff --git a/resources/views/livewire/slow-requests.blade.php b/resources/views/livewire/slow-requests.blade.php index 3a585bc6..c92763a5 100644 --- a/resources/views/livewire/slow-requests.blade.php +++ b/resources/views/livewire/slow-requests.blade.php @@ -2,7 +2,7 @@ @@ -55,6 +55,11 @@ {{ $slowRequest->action }}

@endif + @if (is_array($config['threshold'])) +

+ {{ $slowRequest->threshold }}ms threshold +

+ @endif
@if ($config['sample_rate'] < 1) diff --git a/resources/views/livewire/usage.blade.php b/resources/views/livewire/usage.blade.php index 49609063..d8389ab0 100644 --- a/resources/views/livewire/usage.blade.php +++ b/resources/views/livewire/usage.blade.php @@ -7,7 +7,7 @@ default => 'Application Usage' }" title="Time: {{ number_format($time) }}ms; Run at: {{ $runAt }};" - details="{{ $this->usage === 'slow_requests' ? ($slowRequestsConfig['threshold'].'ms threshold, ') : '' }}past {{ $this->periodForHumans() }}" + details="{{ $this->usage === 'slow_requests' ? (is_array($slowRequestsConfig['threshold']) ? '' : $slowRequestsConfig['threshold'].'ms threshold, ') : '' }}past {{ $this->periodForHumans() }}" > @endif + @if ($this->usage === 'slow_requests' && is_array($slowRequestsConfig['threshold'])) + @php + $message = 'You have per-route thresholds configured.'; + @endphp + + @endif
diff --git a/src/Livewire/SlowJobs.php b/src/Livewire/SlowJobs.php index 492ba399..a6614fc6 100644 --- a/src/Livewire/SlowJobs.php +++ b/src/Livewire/SlowJobs.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\View; +use Laravel\Pulse\Recorders\Concerns\Thresholds; use Laravel\Pulse\Recorders\SlowJobs as SlowJobsRecorder; use Livewire\Attributes\Lazy; use Livewire\Attributes\Url; @@ -15,6 +16,8 @@ #[Lazy] class SlowJobs extends Card { + use Thresholds; + /** * Ordering. * @@ -40,6 +43,7 @@ public function render(): Renderable 'job' => $row->key, 'slowest' => $row->max, 'count' => $row->count, + 'threshold' => $this->threshold($row->key, SlowJobsRecorder::class), ]), $this->orderBy, ); diff --git a/src/Livewire/SlowOutgoingRequests.php b/src/Livewire/SlowOutgoingRequests.php index da1cfbfc..52da83c7 100644 --- a/src/Livewire/SlowOutgoingRequests.php +++ b/src/Livewire/SlowOutgoingRequests.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\View; +use Laravel\Pulse\Recorders\Concerns\Thresholds; use Laravel\Pulse\Recorders\SlowOutgoingRequests as SlowOutgoingRequestsRecorder; use Livewire\Attributes\Lazy; use Livewire\Attributes\Url; @@ -15,6 +16,8 @@ #[Lazy] class SlowOutgoingRequests extends Card { + use Thresholds; + /** * Ordering. * @@ -44,6 +47,7 @@ public function render(): Renderable 'uri' => $uri, 'slowest' => $row->max, 'count' => $row->count, + 'threshold' => $this->threshold($uri, SlowOutgoingRequestsRecorder::class), ]; }), $this->orderBy, diff --git a/src/Livewire/SlowQueries.php b/src/Livewire/SlowQueries.php index e8ff3a5d..b4f1692f 100644 --- a/src/Livewire/SlowQueries.php +++ b/src/Livewire/SlowQueries.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\View; +use Laravel\Pulse\Recorders\Concerns\Thresholds; use Laravel\Pulse\Recorders\SlowQueries as SlowQueriesRecorder; use Livewire\Attributes\Lazy; use Livewire\Attributes\Url; @@ -15,6 +16,8 @@ #[Lazy] class SlowQueries extends Card { + use Thresholds; + /** * Ordering. * @@ -56,6 +59,7 @@ public function render(): Renderable 'location' => $location, 'slowest' => $row->max, 'count' => $row->count, + 'threshold' => $this->threshold($sql, SlowQueriesRecorder::class), ]; }), $this->orderBy, diff --git a/src/Livewire/SlowRequests.php b/src/Livewire/SlowRequests.php index 159e8813..9521f9c2 100644 --- a/src/Livewire/SlowRequests.php +++ b/src/Livewire/SlowRequests.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Support\Renderable; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\View; +use Laravel\Pulse\Recorders\Concerns\Thresholds; use Laravel\Pulse\Recorders\SlowRequests as SlowRequestsRecorder; use Livewire\Attributes\Lazy; use Livewire\Attributes\Url; @@ -15,6 +16,8 @@ #[Lazy] class SlowRequests extends Card { + use Thresholds; + /** * Ordering. * @@ -45,6 +48,7 @@ public function render(): Renderable 'action' => $action, 'count' => $row->count, 'slowest' => $row->max, + 'threshold' => $this->threshold($uri, SlowRequestsRecorder::class), ]; }), $this->orderBy, diff --git a/src/Recorders/Concerns/LivewireRoutes.php b/src/Recorders/Concerns/LivewireRoutes.php index 118e2d93..3d5ff701 100644 --- a/src/Recorders/Concerns/LivewireRoutes.php +++ b/src/Recorders/Concerns/LivewireRoutes.php @@ -20,8 +20,8 @@ protected function resolveRoutePath(Request $request): array $path = $route->getDomain().Str::start($route->uri(), '/'); $via = $route->getActionName(); - if ($route->named('*livewire.update')) { - $snapshot = json_decode($request->input('components.0.snapshot'), flags: JSON_THROW_ON_ERROR); + if ($route->named('*livewire.update') && ($snapshot = $request->input('components.0.snapshot'))) { + $snapshot = json_decode($snapshot, flags: JSON_THROW_ON_ERROR); if (isset($snapshot->memo->path)) { $via = 'via '.$path; diff --git a/src/Recorders/Concerns/Thresholds.php b/src/Recorders/Concerns/Thresholds.php index 4ef2efaa..6f400f85 100644 --- a/src/Recorders/Concerns/Thresholds.php +++ b/src/Recorders/Concerns/Thresholds.php @@ -2,13 +2,36 @@ namespace Laravel\Pulse\Recorders\Concerns; +use Illuminate\Support\Facades\Config; + trait Thresholds { /** * Determine if the duration is under the configured threshold. */ - protected function underThreshold(int|float $duration): bool + protected function underThreshold(int|float $duration, string $key): bool + { + return $duration < $this->threshold($key); + } + + /** + * Get the threshold for the given key. + */ + protected function threshold(string $key, ?string $recorder = null): int { - return $duration < $this->config->get('pulse.recorders.'.static::class.'.threshold'); + $recorder ??= static::class; + + $config = Config::get("pulse.recorders.{$recorder}.threshold"); + + if (! is_array($config)) { + return $config; + } + + // @phpstan-ignore argument.templateType, argument.templateType + $custom = collect($config) + ->except(['default']) + ->first(fn ($threshold, $pattern) => preg_match($pattern, $key) === 1); + + return $custom ?? $config['default'] ?? 1_000; } } diff --git a/src/Recorders/SlowJobs.php b/src/Recorders/SlowJobs.php index e548bb93..e5deac30 100644 --- a/src/Recorders/SlowJobs.php +++ b/src/Recorders/SlowJobs.php @@ -74,9 +74,9 @@ public function record(JobReleasedAfterException|JobFailed|JobProcessed|JobProce $this->pulse->lazy(function () use ($timestamp, $timestampMs, $name, $lastJobStartedProcessingAt) { if ( - $this->underThreshold($duration = $timestampMs - $lastJobStartedProcessingAt) || ! $this->shouldSample() || - $this->shouldIgnore($name) + $this->shouldIgnore($name) || + $this->underThreshold($duration = $timestampMs - $lastJobStartedProcessingAt, $name) ) { return; } diff --git a/src/Recorders/SlowOutgoingRequests.php b/src/Recorders/SlowOutgoingRequests.php index 10d27869..a3b150bc 100644 --- a/src/Recorders/SlowOutgoingRequests.php +++ b/src/Recorders/SlowOutgoingRequests.php @@ -56,9 +56,9 @@ public function record(RequestInterface $request, int $startedAt): void $this->pulse->lazy(function () use ($startedAt, $timestamp, $endedAt, $method, $uri) { if ( - $this->underThreshold($duration = $endedAt - $startedAt) || ! $this->shouldSample() || - $this->shouldIgnore($uri) + $this->shouldIgnore($uri) || + $this->underThreshold($duration = $endedAt - $startedAt, $uri) ) { return; } diff --git a/src/Recorders/SlowQueries.php b/src/Recorders/SlowQueries.php index 6f0b5791..a5950df9 100644 --- a/src/Recorders/SlowQueries.php +++ b/src/Recorders/SlowQueries.php @@ -48,9 +48,9 @@ public function record(QueryExecuted $event): void $this->pulse->lazy(function () use ($timestampMs, $duration, $sql, $location) { if ( - $this->underThreshold($duration) || ! $this->shouldSample() || - $this->shouldIgnore($sql) + $this->shouldIgnore($sql) || + $this->underThreshold($duration, $sql) ) { return; } diff --git a/src/Recorders/SlowRequests.php b/src/Recorders/SlowRequests.php index 44cbbad6..9c4c1473 100644 --- a/src/Recorders/SlowRequests.php +++ b/src/Recorders/SlowRequests.php @@ -50,17 +50,16 @@ public function register(callable $record, Application $app): void */ public function record(Carbon $startedAt, Request $request, Response $response): void { - if ( - ! $request->route() instanceof Route || - $this->underThreshold($duration = ((int) $startedAt->diffInMilliseconds())) || - ! $this->shouldSample() - ) { + if (! $request->route() instanceof Route || ! $this->shouldSample()) { return; } [$path, $via] = $this->resolveRoutePath($request); - if ($this->shouldIgnore($path)) { + if ( + $this->shouldIgnore($path) || + $this->underThreshold($duration = ((int) $startedAt->diffInMilliseconds()), $path) + ) { return; } diff --git a/tests/Feature/Livewire/SlowJobsTest.php b/tests/Feature/Livewire/SlowJobsTest.php index d924a657..00452cab 100644 --- a/tests/Feature/Livewire/SlowJobsTest.php +++ b/tests/Feature/Livewire/SlowJobsTest.php @@ -33,7 +33,7 @@ Livewire::test(SlowJobs::class, ['lazy' => false]) ->assertViewHas('slowJobs', collect([ - (object) ['job' => 'App\Jobs\MyJob', 'count' => 4, 'slowest' => 2468], - (object) ['job' => 'App\Jobs\MyOtherJob', 'count' => 2, 'slowest' => 1234], + (object) ['job' => 'App\Jobs\MyJob', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000], + (object) ['job' => 'App\Jobs\MyOtherJob', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000], ])); }); diff --git a/tests/Feature/Livewire/SlowOutgoingRequestsTest.php b/tests/Feature/Livewire/SlowOutgoingRequestsTest.php index 05bfdf51..937ecc0a 100644 --- a/tests/Feature/Livewire/SlowOutgoingRequestsTest.php +++ b/tests/Feature/Livewire/SlowOutgoingRequestsTest.php @@ -33,7 +33,7 @@ Livewire::test(SlowOutgoingRequests::class, ['lazy' => false]) ->assertViewHas('slowOutgoingRequests', collect([ - (object) ['method' => 'GET', 'uri' => 'http://example.com', 'count' => 4, 'slowest' => 2468], - (object) ['method' => 'GET', 'uri' => 'http://example.org', 'count' => 2, 'slowest' => 1234], + (object) ['method' => 'GET', 'uri' => 'http://example.com', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000], + (object) ['method' => 'GET', 'uri' => 'http://example.org', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000], ])); }); diff --git a/tests/Feature/Livewire/SlowQueriesTest.php b/tests/Feature/Livewire/SlowQueriesTest.php index faf981c5..73a0b8f0 100644 --- a/tests/Feature/Livewire/SlowQueriesTest.php +++ b/tests/Feature/Livewire/SlowQueriesTest.php @@ -36,8 +36,8 @@ Livewire::test(SlowQueries::class, ['lazy' => false]) ->assertViewHas('slowQueries', collect([ - (object) ['sql' => 'select * from `users`', 'location' => 'app/Foo.php:123', 'count' => 4, 'slowest' => 2468], - (object) ['sql' => 'select * from `users` where `id` = ?', 'location' => 'app/Bar.php:456', 'count' => 2, 'slowest' => 1234], + (object) ['sql' => 'select * from `users`', 'location' => 'app/Foo.php:123', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000], + (object) ['sql' => 'select * from `users` where `id` = ?', 'location' => 'app/Bar.php:456', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000], ])); }); diff --git a/tests/Feature/Livewire/SlowRequestsTest.php b/tests/Feature/Livewire/SlowRequestsTest.php index b1d808ef..ee303286 100644 --- a/tests/Feature/Livewire/SlowRequestsTest.php +++ b/tests/Feature/Livewire/SlowRequestsTest.php @@ -36,7 +36,7 @@ Livewire::test(SlowRequests::class, ['lazy' => false]) ->assertViewHas('slowRequests', collect([ - (object) ['method' => 'GET', 'uri' => '/users', 'action' => 'FooController@index', 'count' => 4, 'slowest' => 2468], - (object) ['method' => 'GET', 'uri' => '/users/{user}', 'action' => 'Closure', 'count' => 2, 'slowest' => 1234], + (object) ['method' => 'GET', 'uri' => '/users', 'action' => 'FooController@index', 'count' => 4, 'slowest' => 2468, 'threshold' => 1_000], + (object) ['method' => 'GET', 'uri' => '/users/{user}', 'action' => 'Closure', 'count' => 2, 'slowest' => 1234, 'threshold' => 1_000], ])); }); diff --git a/tests/Feature/Recorders/SlowJobsTest.php b/tests/Feature/Recorders/SlowJobsTest.php index d792baab..3ef36720 100644 --- a/tests/Feature/Recorders/SlowJobsTest.php +++ b/tests/Feature/Recorders/SlowJobsTest.php @@ -85,6 +85,37 @@ expect(Pulse::ignore(fn () => DB::table('pulse_aggregates')->where('type', 'slow_job')->get()))->toHaveCount(0); }); +it('can configure threshold per job', function () { + Carbon::setTestNow('2000-01-02 03:04:05'); + Config::set('queue.default', 'database'); + Config::set('pulse.recorders.'.SlowJobs::class.'.threshold', [ + '#MySlowJob#' => 1_000, + '#AnotherSlowJob#' => 2_000, + ]); + + Bus::dispatchToQueue(new MySlowJob(1_000)); + Bus::dispatchToQueue(new AnotherSlowJob(1_000)); + Artisan::call('queue:work', ['--max-jobs' => 2, '--stop-when-empty' => true, '--sleep' => 0]); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'slow_job')->get()); + expect($entries)->toHaveCount(1); + expect($entries[0]->key)->toBe('MySlowJob'); + expect($entries[0]->value)->toBe(1_000); + + DB::table('pulse_entries')->delete(); + + Bus::dispatchToQueue(new MySlowJob(2_000)); + Bus::dispatchToQueue(new AnotherSlowJob(2_000)); + Artisan::call('queue:work', ['--max-jobs' => 2, '--stop-when-empty' => true, '--sleep' => 0]); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->where('type', 'slow_job')->get()); + expect($entries)->toHaveCount(2); + expect($entries[0]->key)->toBe('MySlowJob'); + expect($entries[0]->value)->toBe(2_000); + expect($entries[1]->key)->toBe('AnotherSlowJob'); + expect($entries[1]->value)->toBe(2_000); +}); + it('can ignore jobs', function () { Config::set('queue.default', 'database'); Config::set('pulse.recorders.'.SlowJobs::class.'.threshold', 0); @@ -220,8 +251,18 @@ class MySlowJob implements ShouldQueue { + public function __construct(public $duration = 100) + { + // + } + public function handle() { - Carbon::setTestNow(Carbon::now()->addMilliseconds(100)); + Carbon::setTestNow(Carbon::now()->addMilliseconds($this->duration)); } } + +class AnotherSlowJob extends MySlowJob +{ + // +} diff --git a/tests/Feature/Recorders/SlowOutgoingRequestsTest.php b/tests/Feature/Recorders/SlowOutgoingRequestsTest.php index 77fe4f6a..925bcb06 100644 --- a/tests/Feature/Recorders/SlowOutgoingRequestsTest.php +++ b/tests/Feature/Recorders/SlowOutgoingRequestsTest.php @@ -4,9 +4,12 @@ use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; +use Illuminate\Support\Sleep; use Laravel\Pulse\Facades\Pulse; use Laravel\Pulse\Recorders\SlowOutgoingRequests; +use function Pest\Laravel\freezeTime; + it('ingests slow outgoing http requests', function () { Config::set('pulse.recorders.'.SlowOutgoingRequests::class.'.threshold', 0); Carbon::setTestNow('2000-01-02 03:04:05'); @@ -219,3 +222,49 @@ expect(Pulse::ingest())->toBe(10); }); + +it('can configure threshold per url', function () { + freezeTime(); + Sleep::fake(syncWithCarbon: true); + Config::set('pulse.recorders.'.SlowOutgoingRequests::class.'.threshold', [ + '#one-second-threshold#' => 1_000, + '#two-second-threshold#' => 2_000, + ]); + $sleepSeconds = null; + Http::fake([ + 'one-second-threshold' => function () use (&$sleepSeconds) { + Sleep::for($sleepSeconds)->seconds(); + + return Http::response('ok'); + }, + 'two-second-threshold' => function () use (&$sleepSeconds) { + Sleep::for($sleepSeconds)->seconds(); + + return Http::response('ok'); + }, + ]); + + $sleepSeconds = 1; + Http::get('one-second-threshold')->throw(); + Http::get('two-second-threshold')->throw(); + Pulse::ingest(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + expect($entries)->toHaveCount(1); + expect($entries[0]->key)->toBe('["GET","one-second-threshold"]'); + expect($entries[0]->value)->toBe(1000); + + DB::table('pulse_entries')->delete(); + + $sleepSeconds = 2; + Http::get('one-second-threshold')->throw(); + Http::get('two-second-threshold')->throw(); + Pulse::ingest(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->orderBy('key')->get()); + expect($entries)->toHaveCount(2); + expect($entries[0]->key)->toBe('["GET","one-second-threshold"]'); + expect($entries[0]->value)->toBe(2_000); + expect($entries[1]->key)->toBe('["GET","two-second-threshold"]'); + expect($entries[1]->value)->toBe(2_000); +}); diff --git a/tests/Feature/Recorders/SlowQueriesTest.php b/tests/Feature/Recorders/SlowQueriesTest.php index 14f06dbe..705cca3a 100644 --- a/tests/Feature/Recorders/SlowQueriesTest.php +++ b/tests/Feature/Recorders/SlowQueriesTest.php @@ -108,6 +108,45 @@ Pulse::ignore(fn () => expect(DB::table('pulse_entries')->count())->toBe(0)); }); +it('can configure threshold per query', function () { + Config::set('pulse.recorders.'.SlowQueries::class.'.threshold', [ + '#one_second_threshold#' => 1_000, + '#two_second_threshold#' => 2_000, + ]); + $queryDuration = null; + prependListener(QueryExecuted::class, function (QueryExecuted $event) use (&$queryDuration) { + $event->time = $queryDuration; + }); + + $queryDuration = 1_000; + DB::pretend(function () { + DB::table('one_second_threshold')->count(); + DB::table('two_second_threshold')->count(); + }); + Pulse::ingest(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + expect($entries)->toHaveCount(1); + expect($entries[0]->key)->toContain('one_second_threshold'); + expect($entries[0]->value)->toBe(1_000); + + DB::table('pulse_entries')->delete(); + + $queryDuration = 2_000; + DB::pretend(function () { + DB::table('one_second_threshold')->count(); + DB::table('two_second_threshold')->count(); + }); + Pulse::ingest(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->orderBy('key')->get()); + expect($entries)->toHaveCount(2); + expect($entries[0]->key)->toContain('one_second_threshold'); + expect($entries[0]->value)->toBe(2_000); + expect($entries[1]->key)->toContain('two_second_threshold'); + expect($entries[1]->value)->toBe(2_000); +}); + it('ingests queries equal to the slow query threshold', function () { Config::set('pulse.recorders.'.SlowQueries::class.'.threshold', 5000); prependListener(QueryExecuted::class, function (QueryExecuted $event) { diff --git a/tests/Feature/Recorders/SlowRequestsTest.php b/tests/Feature/Recorders/SlowRequestsTest.php index e686822d..978d06ba 100644 --- a/tests/Feature/Recorders/SlowRequestsTest.php +++ b/tests/Feature/Recorders/SlowRequestsTest.php @@ -6,6 +6,7 @@ use Illuminate\Support\Facades\Date; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Route; +use Illuminate\Support\Sleep; use Laravel\Pulse\Facades\Pulse; use Laravel\Pulse\Recorders\SlowRequests; use Tests\User; @@ -99,6 +100,53 @@ Pulse::ignore(fn () => expect(DB::table('pulse_values')->count())->toBe(0)); }); +it('can configure threshold per route', function () { + Date::setTestNow('2000-01-02 03:04:05'); + Sleep::fake(syncWithCarbon: true); + Config::set('pulse.recorders.'.SlowRequests::class.'.threshold', [ + '#^/one-second-threshold#' => 1_000, + '#^/two-second-threshold#' => 2_000, + ]); + $sleepSeconds = null; + Route::get('one-second-threshold', function () use (&$sleepSeconds) { + Sleep::for($sleepSeconds)->seconds(); + }); + Route::get('two-second-threshold', function () use (&$sleepSeconds) { + Sleep::for($sleepSeconds)->seconds(); + }); + Route::get('default-threshold', fn () => 'ok'); + + $sleepSeconds = 1; + get('one-second-threshold')->assertOk(); + get('two-second-threshold')->assertOk(); + get('default-threshold')->assertOk(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->get()); + expect($entries)->toHaveCount(1); + expect($entries[0]->type)->toBe('slow_request'); + expect($entries[0]->key)->toBe(json_encode(['GET', '/one-second-threshold', 'Closure'])); + expect($entries[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/one-second-threshold', 'Closure']))); + expect($entries[0]->value)->toBe(1000); + + DB::table('pulse_entries')->delete(); + + $sleepSeconds = 2; + get('one-second-threshold')->assertOk(); + get('two-second-threshold')->assertOk(); + get('default-threshold')->assertOk(); + + $entries = Pulse::ignore(fn () => DB::table('pulse_entries')->orderBy('key')->get()); + expect($entries)->toHaveCount(2); + expect($entries[0]->type)->toBe('slow_request'); + expect($entries[0]->key)->toBe(json_encode(['GET', '/one-second-threshold', 'Closure'])); + expect($entries[0]->key_hash)->toBe(keyHash(json_encode(['GET', '/one-second-threshold', 'Closure']))); + expect($entries[0]->value)->toBe(2000); + expect($entries[1]->type)->toBe('slow_request'); + expect($entries[1]->key)->toBe(json_encode(['GET', '/two-second-threshold', 'Closure'])); + expect($entries[1]->key_hash)->toBe(keyHash(json_encode(['GET', '/two-second-threshold', 'Closure']))); + expect($entries[1]->value)->toBe(2000); +}); + it('captures slow requests per user', function () { Date::setTestNow('2000-01-02 03:04:05'); Config::set('pulse.recorders.'.SlowRequests::class.'.threshold', 0);