Skip to content

Commit

Permalink
Adds new vapor:failed command (#149)
Browse files Browse the repository at this point in the history
* Add vapor:failed command

* Pagination

* Set optional limit

* rename option

* rename command

* add total pages

* Apply fixes from StyleCI

* update pagination

* remove ignore

* Update Request.php

* Apply fixes from StyleCI

---------

Co-authored-by: StyleCI Bot <bot@styleci.io>
Co-authored-by: Nuno Maduro <enunomaduro@gmail.com>
  • Loading branch information
3 people authored Mar 29, 2023
1 parent 6e74713 commit e6a7806
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 5 deletions.
174 changes: 174 additions & 0 deletions src/Console/Commands/VaporQueueListFailedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

namespace Laravel\Vapor\Console\Commands;

use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Queue\ManuallyFailedException;
use Illuminate\Support\Str;

class VaporQueueListFailedCommand extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'vapor:queue-failed
{--limit= : The number of failed jobs to return}
{--page=1 : The page of failed jobs to return}
{--id= : The job ID filter by}
{--queue= : The queue to filter by}
{--query= : The search query to filter by}
{--start= : The start timestamp to filter by}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'List all of the failed queue jobs';

/**
* Indicates whether the command should be shown in the Artisan command list.
*
* @var bool
*/
protected $hidden = true;

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$failed = $this->laravel['queue.failer']->all();

$options = collect($this->options())
->filter(function ($value, $option) {
return ! is_null($value) && in_array($option, ['id', 'queue', 'query', 'start']);
});

$failedJobs = collect($failed)->filter(function ($job) use ($options) {
return $options->every(function ($value, $option) use ($job) {
return $this->filter($job, $option, $value);
});
});

$total = count($failedJobs);

$page = $this->option('page');
$limit = $this->option('limit');

if ($limit) {
$failedJobs = $failedJobs->forPage($page, $limit);
}

$failedJobs = $failedJobs->map(function ($failed) {
return array_merge((array) $failed, [
'payload' => $failed->payload,
'exception' => Str::limit($failed->exception, 1000),
'name' => $this->extractJobName($failed->payload),
'queue' => Str::afterLast($failed->queue, '/'),
'message' => $this->extractMessage($failed->exception),
'connection' => $failed->connection,
]);
})->values()->toArray();

$failedJobs = [
'failed_jobs' => $failedJobs,
'total' => $total,
'from' => $limit ? ($page - 1) * $limit + 1 : 1,
'to' => $limit ? min($page * $limit, $total) : $total,
'has_next_page' => $limit && $total > $limit * $page,
'has_previous_page' => $limit && $page > 1 && $total > $limit * ($page - 1),
];

$this->output->writeln(
json_encode($failedJobs)
);
}

/**
* Extract the failed job name from payload.
*
* @param string $payload
* @return string|null
*/
private function extractJobName($payload)
{
$payload = json_decode($payload, true);

if ($payload && (! isset($payload['data']['command']))) {
return $payload['job'] ?? null;
} elseif ($payload && isset($payload['data']['command'])) {
return $this->matchJobName($payload);
}
}

/**
* Extract the failed job message from exception.
*
* @param string $exception
* @return string
*/
private function extractMessage($exception)
{
if (Str::startsWith($exception, ManuallyFailedException::class)) {
$message = 'Manually failed';
} else {
[$_, $message] = explode(':', $exception);
[$message] = explode(' in /', $message);
[$message] = explode(' in closure', $message);
}

if (! empty($message)) {
return trim($message);
}

return '';
}

/**
* Match the job name from the payload.
*
* @param array $payload
* @return string|null
*/
protected function matchJobName($payload)
{
preg_match('/"([^"]+)"/', $payload['data']['command'], $matches);

return $matches[1] ?? $payload['job'] ?? null;
}

/**
* Determine whether the given job matches the given filter.
*
* @param stdClass $job
* @param string $option
* @param string $value
* @return bool
*/
protected function filter($job, $option, $value)
{
if ($option === 'id') {
return $job->id === $value;
}

if ($option === 'queue') {
return Str::afterLast($job->queue, '/') === $value;
}

if ($option === 'query') {
return Str::contains(json_encode($job), $value);
}

if ($option === 'start') {
return Carbon::parse($job->failed_at)->timestamp >= $value;
}

return false;
}
}
2 changes: 1 addition & 1 deletion src/Logging/JsonFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public function format($record): string
$context = ['aws_request_id' => ($_ENV['AWS_REQUEST_ID'] ?? null)];

if ($record instanceof LogRecord) {
$record = new LogRecord( // @phpstan-ignore-line
$record = new LogRecord(
$record->datetime,
$record->channel,
$record->level,
Expand Down
2 changes: 1 addition & 1 deletion src/Runtime/Octane/Octane.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public static function boot($basePath, $databaseSessionPersist = false, $databas
$databaseSessionTtl = (int) $databaseSessionTtl;

static::$worker = tap(new Worker(
new ApplicationFactory($basePath), new self)
new ApplicationFactory($basePath), new self)
)->boot()->onRequestHandled(static::manageDatabaseSessions($databaseSessionPersist, $databaseSessionTtl));

if ($databaseSessionPersist && $databaseSessionTtl > 0) {
Expand Down
3 changes: 1 addition & 2 deletions src/Runtime/Request.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ class Request
*
* @param array $serverVariables
* @param string $body
* @parama array $headers
*
* @param array $headers
* @return void
*/
public function __construct(array $serverVariables, $body, $headers)
Expand Down
7 changes: 6 additions & 1 deletion src/VaporServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Illuminate\Support\ServiceProvider;
use InvalidArgumentException;
use Laravel\Vapor\Console\Commands\OctaneStatusCommand;
use Laravel\Vapor\Console\Commands\VaporQueueListFailedCommand;
use Laravel\Vapor\Console\Commands\VaporWorkCommand;
use Laravel\Vapor\Http\Controllers\SignedStorageUrlController;
use Laravel\Vapor\Http\Middleware\ServeStaticAssets;
Expand Down Expand Up @@ -163,7 +164,11 @@ protected function registerCommands()
return new VaporWorkCommand($app['queue.vaporWorker']);
});

$this->commands(['command.vapor.work']);
$this->app->singleton('command.vapor.queue-failed', function () {
return new VaporQueueListFailedCommand;
});

$this->commands(['command.vapor.work', 'command.vapor.queue-failed']);
}

/**
Expand Down

0 comments on commit e6a7806

Please sign in to comment.