Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions config/horizon.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@
'monitored' => 10080,
],

/*
|--------------------------------------------------------------------------
| Silenced Jobs
|--------------------------------------------------------------------------
|
| Silencing a job will instruct Horizon to not place the job in the list
| of completed jobs within the Horizon dashboard. This setting may be
| used to fully remove any noisy jobs from the completed jobs list.
|
*/

'silenced' => [
// App\Jobs\ExampleJob::class,
],

/*
|--------------------------------------------------------------------------
| Metrics
Expand Down
2 changes: 1 addition & 1 deletion public/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"/app.js": "/app.js?id=0f8def17167a0224d5704239fefc69ea",
"/app.js": "/app.js?id=c9ab8ad362d7c3abca8809aff9a0e320",
"/app-dark.css": "/app-dark.css?id=796af76ce8c445651baf66c67de3eea4",
"/app.css": "/app.css?id=a19518e2122467d2e842f411ca5b65e4",
"/img/favicon.png": "/img/favicon.png?id=1542bfe8a0010dcbee710da13cce367f",
Expand Down
33 changes: 22 additions & 11 deletions resources/js/screens/recentJobs/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
watch: {
'$route'() {
this.updatePageTitle();

this.page = 1;

this.loadJobs();
Expand Down Expand Up @@ -137,7 +137,11 @@
updatePageTitle() {
document.title = this.$route.params.type == 'pending'
? 'Horizon - Pending Jobs'
: 'Horizon - Completed Jobs';
: (
this.$route.params.type == 'silenced'
? 'Horizon - Silenced Jobs'
: 'Horizon - Completed Jobs'
);
}
}
}
Expand All @@ -149,6 +153,15 @@
<div class="card-header d-flex align-items-center justify-content-between">
<h5 v-if="$route.params.type == 'pending'">Pending Jobs</h5>
<h5 v-if="$route.params.type == 'completed'">Completed Jobs</h5>
<h5 v-if="$route.params.type == 'silenced'">Silenced Jobs</h5>

<router-link v-if="$route.params.type == 'completed'" active-class="active" to="/jobs/silenced" class="nav-link">
<span>View Silenced Jobs</span>
</router-link>

<router-link v-if="$route.params.type == 'silenced'" active-class="active" to="/jobs/completed" class="nav-link">
<span>View Completed Jobs</span>
</router-link>
</div>

<div v-if="!ready"
Expand All @@ -161,21 +174,20 @@
<span>Loading...</span>
</div>


<div v-if="ready && jobs.length == 0"
class="d-flex flex-column align-items-center justify-content-center card-bg-secondary p-5 bottom-radius">
<span>There aren't any jobs.</span>
</div>

<table v-if="ready && jobs.length > 0" class="table table-hover table-sm mb-0">
<thead>
<tr>
<th>Job</th>
<th v-if="$route.params.type=='pending'" class="text-right">Queued At</th>
<th v-if="$route.params.type=='completed'">Queued At</th>
<th v-if="$route.params.type=='completed'">Completed At</th>
<th v-if="$route.params.type=='completed'" class="text-right">Runtime</th>
</tr>
<tr>
<th>Job</th>
<th v-if="$route.params.type=='pending'" class="text-right">Queued At</th>
<th v-if="$route.params.type=='completed' || $route.params.type=='silenced'">Queued At</th>
<th v-if="$route.params.type=='completed' || $route.params.type=='silenced'">Completed At</th>
<th v-if="$route.params.type=='completed' || $route.params.type=='silenced'" class="text-right">Runtime</th>
</tr>
</thead>

<tbody>
Expand All @@ -198,6 +210,5 @@
<button @click="next" class="btn btn-secondary btn-md" :disabled="page>=totalPages">Next</button>
</div>
</div>

</div>
</template>
4 changes: 2 additions & 2 deletions resources/js/screens/recentJobs/job-row.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
{{ readableTimestamp(job.payload.pushedAt) }}
</td>

<td v-if="$route.params.type=='completed'" class="table-fit">
<td v-if="$route.params.type=='completed' || $route.params.type=='silenced'" class="table-fit">
{{ readableTimestamp(job.completed_at) }}
</td>

<td v-if="$route.params.type=='completed'" class="table-fit">
<td v-if="$route.params.type=='completed' || $route.params.type=='silenced'" class="table-fit">
<span>{{ job.completed_at ? (job.completed_at - job.reserved_at).toFixed(2)+'s' : '-' }}</span>
</td>
</tr>
Expand Down
1 change: 1 addition & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
// Job Routes...
Route::get('/jobs/pending', 'PendingJobsController@index')->name('horizon.pending-jobs.index');
Route::get('/jobs/completed', 'CompletedJobsController@index')->name('horizon.completed-jobs.index');
Route::get('/jobs/silenced', 'SilencedJobsController@index')->name('horizon.silenced-jobs.index');
Route::get('/jobs/failed', 'FailedJobsController@index')->name('horizon.failed-jobs.index');
Route::get('/jobs/failed/{id}', 'FailedJobsController@show')->name('horizon.failed-jobs.show');
Route::post('/jobs/retry/{id}', 'RetryController@store')->name('horizon.retry-jobs.show');
Expand Down
18 changes: 17 additions & 1 deletion src/Contracts/JobRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ public function getPending($afterIndex = null);
*/
public function getCompleted($afterIndex = null);

/**
* Get a chunk of silenced jobs.
*
* @param string $afterIndex
* @return \Illuminate\Support\Collection
*/
public function getSilenced($afterIndex = null);

/**
* Get the count of recent jobs.
*
Expand Down Expand Up @@ -88,6 +96,13 @@ public function countPending();
*/
public function countCompleted();

/**
* Get the count of silenced jobs.
*
* @return int
*/
public function countSilenced();

/**
* Get the count of the recently failed jobs.
*
Expand Down Expand Up @@ -159,9 +174,10 @@ public function migrated($connection, $queue, Collection $payloads);
*
* @param \Laravel\Horizon\JobPayload $payload
* @param bool $failed
* @param bool $silenced
* @return void
*/
public function completed(JobPayload $payload, $failed = false);
public function completed(JobPayload $payload, $failed = false, $silenced = false);

/**
* Delete the given monitored jobs by IDs.
Expand Down
62 changes: 62 additions & 0 deletions src/Http/Controllers/SilencedJobsController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Laravel\Horizon\Http\Controllers;

use Illuminate\Http\Request;
use Laravel\Horizon\Contracts\JobRepository;

class SilencedJobsController extends Controller
{
/**
* The job repository implementation.
*
* @var \Laravel\Horizon\Contracts\JobRepository
*/
public $jobs;

/**
* Create a new controller instance.
*
* @param \Laravel\Horizon\Contracts\JobRepository $jobs
* @return void
*/
public function __construct(JobRepository $jobs)
{
parent::__construct();

$this->jobs = $jobs;
}

/**
* Get all of the silenced jobs.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
$jobs = $this->jobs->getSilenced($request->query('starting_at', -1))->map(function ($job) {
$job->payload = json_decode($job->payload);

return $job;
})->values();

return [
'jobs' => $jobs,
'total' => $this->jobs->countSilenced(),
];
}

/**
* Decode the given job.
*
* @param object $job
* @return object
*/
protected function decode($job)
{
$job->payload = json_decode($job->payload);

return $job;
}
}
2 changes: 1 addition & 1 deletion src/Listeners/MarkJobAsComplete.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __construct(JobRepository $jobs, TagRepository $tags)
*/
public function handle(JobDeleted $event)
{
$this->jobs->completed($event->payload, $event->job->hasFailed());
$this->jobs->completed($event->payload, $event->job->hasFailed(), in_array($event->payload->commandName(), config('horizon.silenced', [])));

if (! $event->job->hasFailed() && count($this->tags->monitored($event->payload->tags())) > 0) {
$this->jobs->remember($event->connectionName, $event->queue, $event->payload);
Expand Down
39 changes: 35 additions & 4 deletions src/Repositories/RedisJobRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class RedisJobRepository implements JobRepository
public $pendingJobExpires;

/**
* The number of minutes until completed jobs should be purged.
* The number of minutes until completed and silenced jobs should be purged.
*
* @var int
*/
Expand Down Expand Up @@ -163,6 +163,17 @@ public function getCompleted($afterIndex = null)
return $this->getJobsByType('completed_jobs', $afterIndex);
}

/**
* Get a chunk of silenced jobs.
*
* @param string|null $afterIndex
* @return \Illuminate\Support\Collection
*/
public function getSilenced($afterIndex = null)
{
return $this->getJobsByType('silenced_jobs', $afterIndex);
}

/**
* Get the count of recent jobs.
*
Expand Down Expand Up @@ -203,6 +214,16 @@ public function countCompleted()
return $this->countJobsByType('completed_jobs');
}

/**
* Get the count of silenced jobs.
*
* @return int
*/
public function countSilenced()
{
return $this->countJobsByType('silenced_jobs');
}

/**
* Get the count of the recently failed jobs.
*
Expand Down Expand Up @@ -261,6 +282,8 @@ protected function minutesForType($type)
return $this->pendingJobExpires;
case 'completed_jobs':
return $this->completedJobExpires;
case 'silenced_jobs':
return $this->completedJobExpires;
default:
return $this->recentJobExpires;
}
Expand Down Expand Up @@ -441,16 +464,17 @@ public function migrated($connection, $queue, Collection $payloads)
*
* @param \Laravel\Horizon\JobPayload $payload
* @param bool $failed
* @param bool $silenced
* @return void
*/
public function completed(JobPayload $payload, $failed = false)
public function completed(JobPayload $payload, $failed = false, $silenced = false)
{
if ($payload->isRetry()) {
$this->updateRetryInformationOnParent($payload, $failed);
}

$this->connection()->pipeline(function ($pipe) use ($payload) {
$this->storeJobReference($pipe, 'completed_jobs', $payload);
$this->connection()->pipeline(function ($pipe) use ($payload, $silenced) {
$this->storeJobReference($pipe, $silenced ? 'silenced_jobs' : 'completed_jobs', $payload);
$this->removeJobReference($pipe, 'pending_jobs', $payload);

$pipe->hmset(
Expand Down Expand Up @@ -547,6 +571,12 @@ public function trimRecentJobs()
CarbonImmutable::now()->subMinutes($this->completedJobExpires)->getTimestamp() * -1,
'+inf'
);

$pipe->zremrangebyscore(
'silenced_jobs',
CarbonImmutable::now()->subMinutes($this->completedJobExpires)->getTimestamp() * -1,
'+inf'
);
});
}

Expand Down Expand Up @@ -611,6 +641,7 @@ public function failed($exception, $connection, $queue, JobPayload $payload)
$this->storeJobReference($pipe, 'recent_failed_jobs', $payload);
$this->removeJobReference($pipe, 'pending_jobs', $payload);
$this->removeJobReference($pipe, 'completed_jobs', $payload);
$this->removeJobReference($pipe, 'silenced_jobs', $payload);

$pipe->hmset(
$payload->id(), [
Expand Down
2 changes: 2 additions & 0 deletions webpack.mix.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,5 @@ mix.options({
}),
],
});

mix.disableSuccessNotifications();