Files
citrus-cms/resources/views/admin-ajax/si-queue-information.blade.php
T
2026-04-28 21:15:09 +03:00

289 lines
14 KiB
PHP

<?php
/**
* @api-readonly
* Returns system queue information
*/
use Illuminate\Support\Facades\DB;
// Queue Counts (Waiting)
$queueCounts = DB::table('jobs')
->select('queue', DB::raw('count(*) as total'))
->groupBy('queue')
->get();
// Average Duration from Telescope
$averageDurations = [];
$processedCounts = [];
$globalAvgDuration = 0;
try {
// Check if telescope_entries exists
$entries = DB::table('telescope_entries')
->where('type', 'job')
->where('created_at', '>', now()->subHours(24))
->orderBy('created_at', 'desc')
->limit(500)
->get();
$durations = [];
foreach($entries as $entry) {
$content = json_decode($entry->content, true);
// Telescope content for jobs usually contains 'data' object which has 'queue'
// But for finished jobs, the top level content might have 'queue' and 'duration'
// We'll check both locations to be safe
$queue = $content['queue'] ?? $content['data']['queue'] ?? 'default';
$duration = $content['duration'] ?? null;
if($duration !== null) {
if(!isset($durations[$queue])) {
$durations[$queue] = [];
}
$durations[$queue][] = $duration;
if(!isset($processedCounts[$queue])) $processedCounts[$queue] = 0;
$processedCounts[$queue]++;
}
}
$totalDuration = 0;
$totalCount = 0;
foreach($durations as $queue => $times) {
$count = count($times);
$sum = array_sum($times);
$totalCount += $count;
$totalDuration += $sum;
$avg = $count > 0 ? $sum / $count : 0;
$averageDurations[$queue] = round($avg, 2);
}
$globalAvgDuration = $totalCount > 0 ? round($totalDuration / $totalCount, 2) : 0;
} catch (\Exception $e) {
// Telescope table might be missing
}
?>
<?php
$processed = array_sum($processedCounts);
$pending = DB::table('jobs')->whereNull('reserved_at')->count();
$active = DB::table('jobs')->whereNotNull('reserved_at')->count();
$failed = DB::table('failed_jobs')->count();
$totalWork = $processed + $pending + $active + $failed;
$processedPerc = $totalWork > 0 ? ($processed / $totalWork) * 100 : 0;
$activePerc = $totalWork > 0 ? ($active / $totalWork) * 100 : 0;
$pendingPerc = $totalWork > 0 ? ($pending / $totalWork) * 100 : 0;
$failedPerc = $totalWork > 0 ? ($failed / $totalWork) * 100 : 0;
?>
@if($totalWork > 0)
<div class="row mb-4">
<div class="col-12">
<div class="block block-rounded block-bordered mb-0">
<div class="block-content block-content-full">
<div class="d-flex justify-content-between align-items-center mb-10">
<h4 class="font-w600 mb-0">Overall Queue Progress</h4>
<div class="d-flex align-items-center">
@if($globalAvgDuration > 0)
<span class="badge badge-info font-size-sm mr-10" title="Average duration of last 500 jobs">
<i class="fa fa-hourglass-half mr-5"></i> Average: {{ $globalAvgDuration }} ms
</span>
@endif
<span class="badge badge-primary font-size-sm">{{ round($processedPerc) }}% Completed</span>
</div>
</div>
<div class="progress mb-0" style="height: 20px;">
<div class="progress-bar bg-info" role="progressbar" style="width: {{ $processedPerc }}%;" aria-valuenow="{{ $processedPerc }}" aria-valuemin="0" aria-valuemax="100" title="Processed">
@if($processedPerc > 5) {{ round($processedPerc) }}% @endif
</div>
<div class="progress-bar bg-success progress-bar-striped progress-bar-animated" role="progressbar" style="width: {{ $activePerc }}%;" aria-valuenow="{{ $activePerc }}" aria-valuemin="0" aria-valuemax="100" title="Active">
@if($activePerc > 5) {{ round($activePerc) }}% @endif
</div>
<div class="progress-bar bg-warning" role="progressbar" style="width: {{ $pendingPerc }}%;" aria-valuenow="{{ $pendingPerc }}" aria-valuemin="0" aria-valuemax="100" title="Pending">
@if($pendingPerc > 5) {{ round($pendingPerc) }}% @endif
</div>
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ $failedPerc }}%;" aria-valuenow="{{ $failedPerc }}" aria-valuemin="0" aria-valuemax="100" title="Failed">
@if($failedPerc > 5) {{ round($failedPerc) }}% @endif
</div>
</div>
<div class="d-flex justify-content-between mt-10 font-size-sm text-muted">
<div><i class="fa fa-circle text-info mr-5"></i> Processed ({{$processed}})</div>
<div><i class="fa fa-circle text-success mr-5"></i> Active ({{$active}})</div>
<div><i class="fa fa-circle text-warning mr-5"></i> Pending ({{$pending}})</div>
<div><i class="fa fa-circle text-danger mr-5"></i> Failed ({{$failed}})</div>
</div>
</div>
</div>
</div>
</div>
@endif
<div class="row">
<!-- Pending Jobs -->
<div class="col-md-6 col-xl-3">
<a class="block block-rounded block-bordered block-link-shadow" href="javascript:void(0)">
<div class="block-content block-content-full clearfix">
<div class="float-right mt-15 d-none d-sm-block">
<i class="fa fa-clock fa-2x text-warning-light"></i>
</div>
<div class="font-size-h3 font-w600 text-warning" data-toggle="countTo" data-speed="1000" data-to="{{ $pending }}">
{{ $pending }}
</div>
<div class="font-size-sm font-w600 text-uppercase text-muted">Pending Jobs</div>
</div>
</a>
</div>
<!-- Active Jobs -->
<div class="col-md-6 col-xl-3">
<a class="block block-rounded block-bordered block-link-shadow" href="javascript:void(0)">
<div class="block-content block-content-full clearfix">
<div class="float-right mt-15 d-none d-sm-block">
<i class="fa fa-cog fa-spin fa-2x text-success-light"></i>
</div>
<div class="font-size-h3 font-w600 text-success" data-toggle="countTo" data-speed="1000" data-to="{{ $active }}">
{{ $active }}
</div>
<div class="font-size-sm font-w600 text-uppercase text-muted">Active Jobs</div>
</div>
</a>
</div>
<!-- Failed Jobs -->
<div class="col-md-6 col-xl-3">
<a class="block block-rounded block-bordered block-link-shadow" href="javascript:void(0)">
<div class="block-content block-content-full clearfix">
<div class="float-right mt-15 d-none d-sm-block">
<i class="fa fa-exclamation-circle fa-2x text-danger-light"></i>
</div>
<div class="font-size-h3 font-w600 text-danger" data-toggle="countTo" data-speed="1000" data-to="{{ DB::table('failed_jobs')->count() }}">
{{ DB::table('failed_jobs')->count() }}
</div>
<div class="font-size-sm font-w600 text-uppercase text-muted">Failed Jobs</div>
</div>
</a>
</div>
<!-- Total In Queue -->
<div class="col-md-6 col-xl-3">
<a class="block block-rounded block-bordered block-link-shadow" href="javascript:void(0)">
<div class="block-content block-content-full clearfix">
<div class="float-right mt-15 d-none d-sm-block">
<i class="fa fa-list fa-2x text-info-light"></i>
</div>
<div class="font-size-h3 font-w600 text-info" data-toggle="countTo" data-speed="1000" data-to="{{ $pending + $active }}">
{{ $pending + $active }}
</div>
<div class="font-size-sm font-w600 text-uppercase text-muted">Total In Queue</div>
</div>
</a>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="block block-rounded block-bordered">
<div class="block-header block-header-default">
<h3 class="block-title">
<i class="fa fa-tasks text-muted mr-5"></i> Queue Detailed Metrics
</h3>
</div>
<div class="block-content">
<div class="table-responsive">
<table class="table table-striped table-hover table-vcenter mb-0">
<thead>
<tr>
<th>Queue Name</th>
<th class="text-center">Waiting Processes</th>
<th class="text-center">Processed (Last 500 Jobs)</th>
<th class="text-center">Avg Duration (ms)</th>
<th class="text-center">Status</th>
</tr>
</thead>
<tbody>
@php
// Merge all queues found in waiting and history
$allQueues = $queueCounts->pluck('queue')->toArray();
$allQueues = array_unique(array_merge($allQueues, array_keys($averageDurations)));
@endphp
@forelse($allQueues as $queue)
@php
$waiting = $queueCounts->where('queue', $queue)->first()->total ?? 0;
$avgDuration = $averageDurations[$queue] ?? '-';
$processed = $processedCounts[$queue] ?? 0;
$statusColor = $waiting > 100 ? 'danger' : ($waiting > 10 ? 'warning' : 'success');
@endphp
<tr>
<td class="font-w600">{{ $queue }}</td>
<td class="text-center">
<span class="badge badge-{{ $statusColor }}">{{ $waiting }}</span>
</td>
<td class="text-center">{{ $processed }}</td>
<td class="text-center">{{ $avgDuration }}</td>
<td class="text-center">
@if($waiting > 0)
<i class="fa fa-spinner fa-spin text-warning"></i> Processing
@else
<i class="fa fa-check text-success"></i> Idle
@endif
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted">No queue activity found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="row mt-4">
<div class="col-md-12">
<div class="block block-rounded block-bordered">
<div class="block-header block-header-default">
<h3 class="block-title">
<i class="fa fa-history text-muted mr-5"></i> Recent Failed Jobs
</h3>
</div>
<div class="block-content">
<div class="table-responsive">
<table class="table table-striped table-hover table-vcenter mb-0">
<thead>
<tr>
<th style="width: 200px;">Date</th>
<th style="width: 150px;">Connection</th>
<th style="width: 150px;">Queue</th>
<th>Exception</th>
</tr>
</thead>
<tbody>
@php
$failedJobs = DB::table('failed_jobs')->orderBy('failed_at', 'desc')->limit(10)->get();
@endphp
@forelse($failedJobs as $job)
<tr>
<td class="font-w600">{{ $job->failed_at }}</td>
<td><span class="badge badge-secondary">{{ $job->connection }}</span></td>
<td><span class="badge badge-info">{{ $job->queue }}</span></td>
<td class="text-danger font-size-sm" title="{{ $job->exception }}">{{ substr($job->exception, 0, 150) }}...</td>
</tr>
@empty
<tr>
<td colspan="4" class="text-center py-4 text-muted">
<i class="fa fa-check-circle fa-2x mb-2 text-success"></i><br>
No failed jobs found. System is healthy.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>