Files
FendxPHP/app/Controller/MonitorController.php

498 lines
16 KiB
PHP
Raw Permalink Normal View History

<?php
declare(strict_types=1);
namespace App\Controller;
use Fendx\Core\Annotation\Controller;
use Fendx\Web\Annotation\GetRoute;
use Fendx\Web\Request\Request;
use Fendx\Web\Response\Response;
use Fendx\Monitor\Service\MonitorService;
#[Controller('/monitor')]
class MonitorController
{
#[GetRoute('/health')]
public function health(): array
{
$health = MonitorService::getHealthStatus();
$httpCode = match ($health['status']) {
'healthy' => 200,
'warning' => 200,
'critical' => 503,
default => 200
};
return Response::success($health, 'Health check completed', $httpCode);
}
#[GetRoute('/health/{component}')]
public function healthComponent(string $component): array
{
try {
$health = MonitorService::checkIndividualHealth($component);
$httpCode = match ($health['status']) {
'healthy' => 200,
'warning' => 200,
'critical' => 503,
default => 200
};
return Response::success($health, "Health check for {$component} completed", $httpCode);
} catch (\InvalidArgumentException $e) {
return Response::error(404, "Health check '{$component}' not found");
}
}
#[GetRoute('/health/checks')]
public function healthChecks(): array
{
$checks = MonitorService::getAvailableHealthChecks();
return Response::success($checks);
}
#[GetRoute('/metrics')]
public function metrics(Request $request): array
{
$format = $request->get('format', 'json');
if ($format === 'prometheus') {
header('Content-Type: text/plain; version=0.0.4');
echo MonitorService::exportMetrics('prometheus');
exit;
}
$metrics = MonitorService::getMetricsSummary();
return Response::success($metrics);
}
#[GetRoute('/alerts')]
public function alerts(): array
{
$alerts = MonitorService::getAlerts();
return Response::success($alerts);
}
#[GetRoute('/alerts/active')]
public function activeAlerts(): array
{
$alerts = MonitorService::getActiveAlerts();
return Response::success($alerts);
}
#[PostRoute('/alerts/{alertId}/acknowledge')]
public function acknowledgeAlert(string $alertId, Request $request): array
{
$data = $request->all();
$acknowledgedBy = $data['acknowledged_by'] ?? 'system';
$success = MonitorService::acknowledgeAlert($alertId, $acknowledgedBy);
if ($success) {
return Response::success(null, 'Alert acknowledged');
} else {
return Response::error(404, 'Alert not found or already acknowledged');
}
}
#[PostRoute('/alerts/{alertId}/resolve')]
public function resolveAlert(string $alertId): array
{
$success = MonitorService::resolveAlert($alertId);
if ($success) {
return Response::success(null, 'Alert resolved');
} else {
return Response::error(404, 'Alert not found');
}
}
#[GetRoute('/errors')]
public function errors(Request $request): array
{
$filters = [];
if ($type = $request->get('type')) {
$filters['type'] = $type;
}
if ($severity = $request->get('severity')) {
$filters['severity'] = $severity;
}
$errors = MonitorService::getErrors($filters);
return Response::success($errors);
}
#[GetRoute('/errors/statistics')]
public function errorStatistics(): array
{
$stats = MonitorService::getErrorStatistics();
return Response::success($stats);
}
#[GetRoute('/errors/trends')]
public function errorTrends(Request $request): array
{
$hours = (int)($request->get('hours', 24));
$trends = MonitorService::getErrorTrends($hours);
return Response::success($trends);
}
#[GetRoute('/logs/search')]
public function searchLogs(Request $request): array
{
$criteria = [
'level' => $request->get('level'),
'message' => $request->get('message'),
'trace_id' => $request->get('trace_id'),
'start_time' => $request->get('start_time'),
'end_time' => $request->get('end_time'),
'pattern' => $request->get('pattern'),
'limit' => (int)($request->get('limit', 100)),
'offset' => (int)($request->get('offset', 0)),
'sort' => $request->get('sort', 'desc'),
'sort_by' => $request->get('sort_by', 'timestamp')
];
$results = MonitorService::searchLogs($criteria);
return Response::success($results);
}
#[GetRoute('/logs/aggregate')]
public function aggregateLogs(Request $request): array
{
$criteria = [
'time_range' => $request->get('time_range', '1h'),
'group_by' => $request->get('group_by', 'level')
];
$results = MonitorService::aggregateLogs($criteria);
return Response::success($results);
}
#[GetRoute('/logs/files')]
public function getLogFiles(): array
{
$files = MonitorService::getLogFiles();
return Response::success($files);
}
#[GetRoute('/logs/content')]
public function getLogContent(Request $request): array
{
$file = $request->get('file');
$lines = (int)($request->get('lines', 100));
$offset = (int)($request->get('offset', 0));
if (!$file) {
return Response::error(400, 'File parameter is required');
}
$content = MonitorService::getLogContent($file, $lines, $offset);
return Response::success($content);
}
#[GetRoute('/logs/realtime')]
public function getRealTimeLogs(Request $request): array
{
$tail = (int)($request->get('tail', 100));
$logs = MonitorService::getRealTimeLogs($tail);
return Response::success($logs);
}
#[GetRoute('/logs/export')]
public function exportLogs(Request $request): void
{
$criteria = [
'level' => $request->get('level'),
'message' => $request->get('message'),
'start_time' => $request->get('start_time'),
'end_time' => $request->get('end_time'),
'limit' => (int)($request->get('limit', 1000))
];
$format = $request->get('format', 'json');
$content = MonitorService::exportLogs($criteria, $format);
$filename = 'logs_' . date('Y-m-d_H-i-s') . '.' . $format;
header('Content-Type: ' . match ($format) {
'json' => 'application/json',
'csv' => 'text/csv',
'txt' => 'text/plain',
default => 'application/octet-stream'
});
header('Content-Disposition: attachment; filename="' . $filename . '"');
echo $content;
exit;
}
#[GetRoute('/logs/charts/{chartType}')]
public function generateLogChart(string $chartType, Request $request): void
{
$data = [];
$title = $request->get('title', '');
switch ($chartType) {
case 'timeline':
case 'error_trend':
$timeRange = $request->get('time_range', '1h');
$aggregation = MonitorService::aggregateLogs(['time_range' => $timeRange]);
$data = $aggregation['timeline'] ?? [];
break;
case 'level_distribution':
$aggregation = MonitorService::aggregateLogs();
$data = $aggregation['groups'] ?? [];
break;
case 'top_errors':
$aggregation = MonitorService::aggregateLogs();
$data = $aggregation['top_errors'] ?? [];
break;
case 'bar':
$aggregation = MonitorService::aggregateLogs();
$data = $aggregation['groups'] ?? [];
$title = $title ?: 'Log Distribution';
break;
default:
$data = [];
}
$chart = MonitorService::generateLogChart($chartType, $data, $title);
header('Content-Type: image/svg+xml');
header('Cache-Control: no-cache');
echo $chart;
exit;
}
#[GetRoute('/stats')]
public function stats(): array
{
$metrics = MonitorService::getMetricsSummary();
$stats = [
'system' => $metrics['system'] ?? [],
'http_requests' => $this->extractHttpStats($metrics),
'database' => $this->extractDatabaseStats($metrics),
'cache' => $this->extractCacheStats($metrics),
'errors' => $this->extractErrorStats($metrics),
'performance' => $this->extractPerformanceStats($metrics)
];
return Response::success($stats);
}
#[GetRoute('/dashboard')]
public function dashboard(): array
{
$health = MonitorService::getHealthStatus();
$metrics = MonitorService::getMetricsSummary();
$alerts = MonitorService::getAlerts();
$dashboard = [
'overview' => [
'status' => $health['status'],
'uptime' => $metrics['system']['uptime'] ?? 0,
'memory_usage' => $metrics['system']['memory_usage'] ?? 0,
'cpu_usage' => $metrics['system']['cpu_usage'] ?? 0,
'active_alerts' => count($alerts)
],
'health_checks' => $health['checks'],
'metrics' => [
'requests_total' => $this->getTotalRequests($metrics),
'errors_total' => $this->getTotalErrors($metrics),
'avg_response_time' => $this->getAverageResponseTime($metrics),
'success_rate' => $this->getSuccessRate($metrics)
],
'recent_alerts' => array_slice($alerts, 0, 10)
];
return Response::success($dashboard);
}
#[GetRoute('/clear')]
public function clear(): array
{
MonitorService::clearMetrics();
return Response::success(null, 'Metrics cleared');
}
private function extractHttpStats(array $metrics): array
{
$httpStats = [];
if (isset($metrics['histograms']['http_request_duration'])) {
$httpStats['duration'] = $metrics['histograms']['http_request_duration'];
}
if (isset($metrics['counters'])) {
$httpStats['requests_by_status'] = [];
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'http_requests_total')) {
$httpStats['requests_by_status'][$key] = $value;
}
}
}
return $httpStats;
}
private function extractDatabaseStats(array $metrics): array
{
$dbStats = [];
if (isset($metrics['histograms']['db_query_duration'])) {
$dbStats['query_duration'] = $metrics['histograms']['db_query_duration'];
}
if (isset($metrics['counters'])) {
$dbStats['queries_by_type'] = [];
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'db_queries_total')) {
$dbStats['queries_by_type'][$key] = $value;
}
}
}
return $dbStats;
}
private function extractCacheStats(array $metrics): array
{
$cacheStats = [];
if (isset($metrics['counters'])) {
$cacheStats['operations'] = [];
$totalHits = 0;
$totalMisses = 0;
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'cache_operations_total')) {
$cacheStats['operations'][$key] = $value;
if (str_contains($key, 'hit:true')) {
$totalHits += $value;
} elseif (str_contains($key, 'hit:false')) {
$totalMisses += $value;
}
}
}
$total = $totalHits + $totalMisses;
$cacheStats['hit_rate'] = $total > 0 ? round(($totalHits / $total) * 100, 2) . '%' : '0%';
}
return $cacheStats;
}
private function extractErrorStats(array $metrics): array
{
$errorStats = [];
if (isset($metrics['counters'])) {
$errorStats['errors_by_type'] = [];
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'errors_total')) {
$errorStats['errors_by_type'][$key] = $value;
}
}
}
if (isset($metrics['metrics']['error'])) {
$errorStats['recent_errors'] = array_slice($metrics['metrics']['error'], -10);
}
return $errorStats;
}
private function extractPerformanceStats(array $metrics): array
{
$perfStats = [
'memory' => $metrics['system'] ?? [],
'response_times' => [],
'throughput' => []
];
if (isset($metrics['histograms']['http_request_duration'])) {
$perfStats['response_times'] = $metrics['histograms']['http_request_duration'];
}
// 计算吞吐量(每秒请求数)
if (isset($metrics['metrics']['http_requests_total'])) {
$requests = $metrics['metrics']['http_requests_total'];
$now = microtime(true);
$requestsInLastMinute = 0;
foreach ($requests as $request) {
if ($now - $request['timestamp'] < 60) {
$requestsInLastMinute++;
}
}
$perfStats['throughput']['requests_per_minute'] = $requestsInLastMinute;
$perfStats['throughput']['requests_per_second'] = round($requestsInLastMinute / 60, 2);
}
return $perfStats;
}
private function getTotalRequests(array $metrics): int
{
$total = 0;
if (isset($metrics['counters'])) {
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'http_requests_total')) {
$total += (int)$value;
}
}
}
return $total;
}
private function getTotalErrors(array $metrics): int
{
$total = 0;
if (isset($metrics['counters'])) {
foreach ($metrics['counters'] as $key => $value) {
if (str_contains($key, 'errors_total')) {
$total += (int)$value;
}
}
}
return $total;
}
private function getAverageResponseTime(array $metrics): float
{
if (isset($metrics['histograms']['http_request_duration']['mean'])) {
return round($metrics['histograms']['http_request_duration']['mean'], 3);
}
return 0.0;
}
private function getSuccessRate(array $metrics): string
{
$totalRequests = $this->getTotalRequests($metrics);
$totalErrors = $this->getTotalErrors($metrics);
if ($totalRequests === 0) {
return '100%';
}
$successRate = (($totalRequests - $totalErrors) / $totalRequests) * 100;
return round($successRate, 2) . '%';
}
}