Skip to content

Commit

Permalink
Merge e76be54 into b3fde76
Browse files Browse the repository at this point in the history
  • Loading branch information
Okipa committed May 6, 2020
2 parents b3fde76 + e76be54 commit 343d37b
Show file tree
Hide file tree
Showing 18 changed files with 192 additions and 238 deletions.
8 changes: 1 addition & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
# Changelog

## [3.0.1](https://github.com/Okipa/laravel-stuck-jobs-notifier/compare/3.0.0...3.0.1)

2020-04-05

* Fixed default notification messages.

## [3.0.0](https://github.com/Okipa/laravel-stuck-jobs-notifier/compare/2.0.0...3.0.0)

2020-04-05
2020-04-06

* Changed the name of the package.
* Changed the command signature.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
[![Latest Version](https://img.shields.io/github/release/okipa/laravel-stuck-jobs-notifier.svg?style=flat-square)](https://github.com/Okipa/laravel-stuck-jobs-notifier/releases)
[![Total Downloads](https://img.shields.io/packagist/dt/okipa/laravel-stuck-jobs-notifier.svg?style=flat-square)](https://packagist.org/packages/okipa/laravel-stuck-jobs-notifier)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://travis-ci.org/Okipa/laravel-stuck-jobs-notifier.svg?branch=master)](https://travis-ci.org/Okipa/laravel-stuck-jobs-notifier)
[![Build status](https://github.com/Okipa/laravel-stuck-jobs-notifier/workflows/CI/badge.svg)](https://github.com/Okipa/laravel-stuck-jobs-notifier/actions)
[![Coverage Status](https://coveralls.io/repos/github/Okipa/laravel-stuck-jobs-notifier/badge.svg?branch=master)](https://coveralls.io/github/Okipa/laravel-stuck-jobs-notifier?branch=master)
[![Quality Score](https://img.shields.io/scrutinizer/g/Okipa/laravel-stuck-jobs-notifier.svg?style=flat-square)](https://scrutinizer-ci.com/g/Okipa/laravel-stuck-jobs-notifier/?branch=master)

Get notified and execute any PHP callback when some jobs are stuck in your `failed_jobs` table for a number of hours of your choice.
Get notified and execute PHP callback when you have stuck jobs for a defined number of hours.

Notifications can be sent by mail, Slack and webhooks (chats often provide a webhook API).

Expand Down
13 changes: 7 additions & 6 deletions config/stuck-jobs-notifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@
'hours_limit' => 2,

/*
* The notifiable to which the notification will be sent. The default
* notifiable will use the mail and slack configuration specified
* in this config file.
* The notifiable to which the notification will be sent.
* The default notifiable will use the mail, slack and webhook configuration specified in this config file.
* You may use your own notifiable but make sure it extends this one.
*/
'notifiable' => Okipa\LaravelStuckJobsNotifier\Notifiable::class,

/*
* The notification that will be sent when stuck jobs are detected.
* You may use your own notification but make sure it extends this one.
*/
'notification' => Okipa\LaravelStuckJobsNotifier\Notification::class,
'notification' => Okipa\LaravelStuckJobsNotifier\Notifications\JobsAreStuck::class,

/*
* The callback that will be executed when stuck jobs are detected.
* Any class can be called here and will receive a $stuckJobs collection in its constructor.
* You may use your own callback but make sure it extends this one.
* Can be set to null if you do not want any callback to be executed.
*/
'callback' => Okipa\LaravelStuckJobsNotifier\Callback::class,
'callback' => Okipa\LaravelStuckJobsNotifier\Callbacks\OnStuckJobs::class,

/*
* You can pass a boolean or a callable to authorize or block the notification process.
Expand Down
8 changes: 3 additions & 5 deletions src/Callback.php → src/Callbacks/OnStuckJobs.php
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<?php

namespace Okipa\LaravelStuckJobsNotifier;
namespace Okipa\LaravelStuckJobsNotifier\Callbacks;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Okipa\LaravelStuckJobsNotifier\Exceptions\StuckJobsDetected;

class Callback
class OnStuckJobs
{
/**
* Callback constructor.
*
* @param \Illuminate\Support\Collection $stuckJobs
*
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\StuckJobsDetected
*/
public function __construct(Collection $stuckJobs)
public function __invoke(Collection $stuckJobs)
{
$stuckJobsCount = $stuckJobs->count();
throw new StuckJobsDetected($stuckJobsCount . ' stuck failed '
Expand Down
1 change: 0 additions & 1 deletion src/Commands/NotifyStuckJobs.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ class NotifyStuckJobs extends Command
/**
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InexistentFailedJobsTable
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidHoursLimit
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidNotification
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidAllowedToRun
*/
public function handle(): void
Expand Down
10 changes: 0 additions & 10 deletions src/Exceptions/InvalidNotification.php

This file was deleted.

78 changes: 0 additions & 78 deletions src/Notification.php

This file was deleted.

110 changes: 110 additions & 0 deletions src/Notifications/JobsAreStuck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php

namespace Okipa\LaravelStuckJobsNotifier\Notifications;

use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification as IlluminateNotification;
use Illuminate\Support\Collection;
use NotificationChannels\Webhook\WebhookMessage;

class JobsAreStuck extends IlluminateNotification
{
protected Collection $stuckJobs;

protected int $stuckJobsCount;

public function __construct(Collection $stuckJobs)
{
$this->stuckJobs = $stuckJobs;
$this->stuckJobsCount = $stuckJobs->count();
}

/**
* Get the notification's delivery channels.
*
* @return array
*/
public function via(): array
{
return config('stuck-jobs-notifier.channels');
}

/**
* Get the mail representation of the notification.
*
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail(): MailMessage
{
return (new MailMessage)->level('error')
->subject(trans_choice(
'{1}:app - :env: :count stuck job detected|[2,*]:app - :env: :count stuck jobs detected',
$this->stuckJobsCount,
[
'app' => config('app.name'),
'env' => config('app.env'),
'count' => $this->stuckJobsCount,
]
))
->line(trans_choice(
'{1}We have detected that :count job is stuck since :date on [:app - :env](:url).'
. '|[2,*]We have detected that :count jobs are stuck since :date on [:app - :env](:url).',
$this->stuckJobsCount,
[
'count' => $this->stuckJobsCount,
'date' => $this->stuckJobs->min('failed_at')->format('d/m/Y - H:i:s'),
'app' => config('app.name'),
'env' => config('app.env'),
'url' => config('app.url'),
]
))
->line('Please check your stuck jobs connecting to your server and executing the '
. '"php artisan queue:failed" command.');
}

/**
* Get the slack representation of the notification.
*
* @return \Illuminate\Notifications\Messages\SlackMessage
*/
public function toSlack(): SlackMessage
{
return (new SlackMessage)->error()->content('⚠ ' . trans_choice(
'{1}`:app - :env` :count stuck job detected on :url since :date.'
. '|[2,*]`:app - :env` :count stuck jobs detected on :url since :date.',
$this->stuckJobsCount,
[
'app' => config('app.name'),
'env' => config('app.env'),
'count' => $this->stuckJobsCount,
'url' => config('app.url'),
'date' => $this->stuckJobs->min('failed_at')->format('d/m/Y - H:i:s'),
]
));
}

/**
* Get the webhook representation of the notification.
*
* @return \NotificationChannels\Webhook\WebhookMessage
*/
public function toWebhook(): WebhookMessage
{
// rocket chat webhook example
return WebhookMessage::create()->data([
'text' => '⚠ ' . trans_choice(
'{1}`:app - :env` :count stuck job detected on :url since :date.'
. '|[2,*]`:app - :env` :count stuck jobs detected on :url since :date.',
$this->stuckJobsCount,
[
'app' => config('app.name'),
'env' => config('app.env'),
'count' => $this->stuckJobsCount,
'url' => config('app.url'),
'date' => $this->stuckJobs->min('failed_at')->format('d/m/Y - H:i:s'),
]
),
])->header('Content-Type', 'application/json');
}
}
47 changes: 20 additions & 27 deletions src/StuckJobsNotifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@
namespace Okipa\LaravelStuckJobsNotifier;

use Carbon\Carbon;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Okipa\LaravelStuckJobsNotifier\Callbacks\OnStuckJobs;
use Okipa\LaravelStuckJobsNotifier\Exceptions\InexistentFailedJobsTable;
use Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidAllowedToRun;
use Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidCallback;
use Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidHoursLimit;
use Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidNotification;
use Okipa\LaravelStuckJobsNotifier\Notifications\JobsAreStuck;

class StuckJobsNotifier
{
/**
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InexistentFailedJobsTable
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidAllowedToRun
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidCallback
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidHoursLimit
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidNotification
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\StuckJobsDetected
*/
public function notify(): void
{
if ($this->isAllowedToRun()) {
$stuckJobs = $this->getStuckFailedJobs();
if ($stuckJobs->isNotEmpty()) {
$notifiable = app(config('stuck-jobs-notifier.notifiable'));
$notification = $this->getNotification($stuckJobs);
$notifiable->notify($notification);
$this->executeCallback($stuckJobs);
$this->getNotifiable()->notify($notification);
$callback = $this->getCallback();
if ($callback) {
$callback($stuckJobs);
}
}
}
}
Expand All @@ -47,7 +47,8 @@ public function isAllowedToRun(): bool
} elseif (is_bool($allowedToRun)) {
return $allowedToRun;
}
throw new InvalidAllowedToRun('The `allowed_to_run` config is not a boolean or a callable.');
throw new InvalidAllowedToRun('The `stuck-jobs-notifier.allowed_to_run` config is not a boolean or '
. 'a callable.');
}

/**
Expand Down Expand Up @@ -93,34 +94,26 @@ public function getHoursLimit(): int
{
$hoursLimit = config('stuck-jobs-notifier.hours_limit');
if (! is_int($hoursLimit)) {
throw new InvalidHoursLimit('The `hours_limit` config is not an integer.');
throw new InvalidHoursLimit('The `stuck-jobs-notifier.hours_limit` config is not an integer.');
}

return $hoursLimit;
}

/**
* @param \Illuminate\Support\Collection $stuckJobs
*
* @return \Illuminate\Notifications\Notification
* @throws \Okipa\LaravelStuckJobsNotifier\Exceptions\InvalidNotification
*/
public function getNotification(Collection $stuckJobs): Notification
public function getNotification(Collection $stuckJobs): JobsAreStuck
{
/** @var \Okipa\LaravelStuckJobsNotifier\Notification|mixed $notification */
$notification = app(config('stuck-jobs-notifier.notification'), ['stuckJobs' => $stuckJobs]);
if (! $notification instanceof Notification || ! is_subclass_of($notification, Notification::class)) {
throw new InvalidNotification('The `notification` config does not extend ' . Notification::class);
}
return app(config('stuck-jobs-notifier.notification'), ['stuckJobs' => $stuckJobs]);
}

return $notification;
public function getNotifiable(): Notifiable
{
return app(config('stuck-jobs-notifier.notifiable'));
}

public function executeCallback(Collection $stuckJobs): void
public function getCallback(): ?OnStuckJobs
{
$callback = config('stuck-jobs-notifier.callback');
if ($callback) {
new $callback($stuckJobs);
}

return $callback ? app($callback) : null;
}
}

0 comments on commit 343d37b

Please sign in to comment.