<?php
namespace Laravel\Pulse\Recorders;
use Carbon\CarbonImmutable;
use Illuminate\Queue\Events\JobFailed;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Queue\Events\JobProcessing;
use Illuminate\Queue\Events\JobReleasedAfterException;
use Laravel\Pulse\Pulse;
/**
* @internal
*/
class SlowJobs
{
use Concerns\Ignores, Concerns\Sampling, Concerns\Thresholds;
/**
* The time the last job started processing.
*/
protected ?int $lastJobStartedProcessingAt = null;
/**
* The events to listen for.
*
* @var list<class-string>
*/
public array $listen = [
JobReleasedAfterException::class,
JobFailed::class,
JobProcessed::class,
JobProcessing::class,
];
/**
* Create a new recorder instance.
*/
public function __construct(
protected Pulse $pulse,
) {
//
}
/**
* Record the job.
*/
public function record(JobReleasedAfterException|JobFailed|JobProcessed|JobProcessing $event): void
{
if ($event->connectionName === 'sync') {
return;
}
$now = CarbonImmutable::now();
if ($event instanceof JobProcessing) {
$this->lastJobStartedProcessingAt = $now->getTimestampMs();
return;
}
if ($this->lastJobStartedProcessingAt === null) {
return;
}
[$timestamp, $timestampMs, $name, $lastJobStartedProcessingAt] = [
$now->getTimestamp(),
$now->getTimestampMs(),
$event->job->resolveName(),
tap($this->lastJobStartedProcessingAt, fn () => ($this->lastJobStartedProcessingAt = null)),
];
$this->pulse->lazy(function () use ($timestamp, $timestampMs, $name, $lastJobStartedProcessingAt) {
if (
! $this->shouldSample() ||
$this->shouldIgnore($name) ||
$this->underThreshold($duration = $timestampMs - $lastJobStartedProcessingAt, $name)
) {
return;
}
$this->pulse->record(
type: 'slow_job',
key: $name,
value: $duration,
timestamp: $timestamp,
)->max()->count();
});
}
}