presseportale/app/Console/Commands/ReportAdminSlowRequests.php
Kevin Adametz 5b8bdf4182
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
12-05-2026 Frontend dev
2026-05-12 18:32:33 +02:00

181 lines
6.2 KiB
PHP

<?php
namespace App\Console\Commands;
use App\Services\Admin\AdminSlowRequestReporter;
use Illuminate\Console\Command;
class ReportAdminSlowRequests extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'admin:slow-requests
{--from= : Startzeitpunkt, z.B. 2026-04-29 oder 2026-04-29 08:00:00}
{--to= : Endzeitpunkt}
{--route= : Auf Route-Namen filtern}
{--path= : Auf Pfad filtern}
{--status= : Auf HTTP-Statuscode filtern}
{--min-duration= : Mindestdauer in Millisekunden}
{--top=10 : Anzahl Top-Zeilen pro Abschnitt}
{--limit=25 : Anzahl Detailzeilen}
{--file=* : Optionaler Logdatei-Pfad oder Glob}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Wertet Slow-Admin-Request-Logs aus.';
/**
* Execute the console command.
*/
public function handle(AdminSlowRequestReporter $reporter): int
{
$top = max(1, (int) $this->option('top'));
$limit = max(1, (int) $this->option('limit'));
$files = array_values(array_filter(array_map('strval', (array) $this->option('file'))));
$report = $reporter->report(
filters: [
'from' => $this->option('from') !== null ? (string) $this->option('from') : null,
'to' => $this->option('to') !== null ? (string) $this->option('to') : null,
'route' => $this->option('route') !== null ? (string) $this->option('route') : null,
'path' => $this->option('path') !== null ? (string) $this->option('path') : null,
'status' => $this->option('status') !== null ? (int) $this->option('status') : null,
'min_duration_ms' => $this->option('min-duration') !== null ? (int) $this->option('min-duration') : null,
],
top: $top,
limit: $limit,
paths: $files !== [] ? $files : null,
);
$this->info('Slow-Admin-Request-Report');
$this->newLine();
$this->line("Dateien: {$report['summary']['files']}");
$this->line("Requests: {$report['summary']['total_requests']}");
$this->line("Routen: {$report['summary']['unique_routes']}");
$this->line("Durchschnitt Dauer: {$report['summary']['average_duration_ms']} ms");
$this->line("Max. Dauer: {$report['summary']['max_duration_ms']} ms");
$this->line("Durchschnitt DB-Zeit: {$report['summary']['average_database_time_ms']} ms");
$this->line("Max. Query-Anzahl: {$report['summary']['max_query_count']}");
$this->renderTopTable('Top Routen', $report['top_routes']);
$this->renderTopTable('Top Pfade', $report['top_paths']);
$this->renderTopTable('Statuscodes', $report['status_codes']);
$this->renderRequests('Langsamste Requests', $report['slowest_requests']);
$this->renderRequests('Query-lastige Requests', $report['query_heavy_requests']);
$this->renderSlowQueries($report['slow_queries']);
$this->renderExplainPlans($report['explain_plans']);
return self::SUCCESS;
}
/**
* @param list<array<string, mixed>> $rows
*/
private function renderTopTable(string $title, array $rows): void
{
if ($rows === []) {
return;
}
$this->newLine();
$this->line($title);
$this->table(
['Wert', 'Requests', 'Ø Dauer', 'Max Dauer', 'Ø DB', 'Queries'],
collect($rows)
->map(fn (array $row): array => [
$row['value'],
$row['requests'],
$row['average_duration_ms'].' ms',
$row['max_duration_ms'].' ms',
$row['average_database_time_ms'].' ms',
$row['total_queries'],
])
->all()
);
}
/**
* @param list<array<string, mixed>> $rows
*/
private function renderRequests(string $title, array $rows): void
{
if ($rows === []) {
return;
}
$this->newLine();
$this->line($title);
$this->table(
['Zeit', 'Route', 'Pfad', 'Status', 'Dauer', 'DB', 'Queries'],
collect($rows)
->map(fn (array $row): array => [
$row['timestamp'],
$row['route_name'],
$row['path'],
$row['status_code'],
$row['duration_ms'].' ms',
$row['database_time_ms'].' ms',
$row['query_count'],
])
->all()
);
}
/**
* @param list<array<string, mixed>> $rows
*/
private function renderSlowQueries(array $rows): void
{
if ($rows === []) {
return;
}
$this->newLine();
$this->line('Häufige Slow Queries');
$this->table(
['SQL', 'Vorkommen', 'Ø Zeit', 'Max Zeit'],
collect($rows)
->map(fn (array $row): array => [
str($row['sql'])->limit(100)->toString(),
$row['occurrences'],
$row['average_time_ms'].' ms',
$row['max_time_ms'].' ms',
])
->all()
);
}
/**
* @param list<array<string, mixed>> $rows
*/
private function renderExplainPlans(array $rows): void
{
if ($rows === []) {
return;
}
$this->newLine();
$this->line('EXPLAIN Top Slow Queries');
foreach ($rows as $row) {
$this->line(str($row['sql'])->limit(120)->toString());
if ($row['error'] !== null) {
$this->warn((string) $row['error']);
continue;
}
$this->table(
array_keys($row['plan'][0] ?? []),
collect($row['plan'])->map(fn (array $planRow): array => array_values($planRow))->all()
);
}
}
}