Files
michaelschiemer/src/Framework/LiveComponents/Controllers/LiveComponentMonitoringController.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

330 lines
10 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Controllers;
use App\Framework\Attributes\Route;
use App\Framework\Auth\Auth;
use App\Framework\Http\Enums\Method;
use App\Framework\Http\Result\JsonResult;
use App\Framework\LiveComponents\Cache\CacheMetricsCollector;
use App\Framework\LiveComponents\Cache\ComponentStateCache;
use App\Framework\LiveComponents\ComponentRegistry;
use App\Framework\LiveComponents\Performance\ComponentMetadataCache;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\View\ProcessorPerformanceTracker;
/**
* LiveComponent Monitoring Controller
*
* Production monitoring endpoints for LiveComponents system.
*
* Features:
* - Cache metrics and performance assessment
* - Component registry statistics
* - Template processor performance (if enabled)
* - System health status
* - Performance warnings and alerts
* - Component inspection for debugging
*
* Security: All endpoints require admin authentication
*/
final readonly class LiveComponentMonitoringController
{
public function __construct(
private CacheMetricsCollector $metricsCollector,
private ComponentRegistry $registry,
private ComponentMetadataCache $metadataCache,
private ComponentStateCache $stateCache,
private ?ProcessorPerformanceTracker $performanceTracker = null
) {
}
/**
* Get comprehensive metrics
*
* Returns detailed metrics for all caching layers, component registry,
* and optional template processor performance.
*
* @route GET /api/livecomponents/metrics
*/
#[Route('/api/livecomponents/metrics', method: Method::GET)]
#[Auth(roles: ['admin'])]
public function metrics(): JsonResult
{
$metrics = [
'cache' => $this->getCacheMetrics(),
'registry' => $this->getRegistryStats(),
'timestamp' => time(),
'system' => [
'memory_usage' => memory_get_usage(true),
'peak_memory' => memory_get_peak_usage(true),
],
];
// Include processor performance if tracking is enabled
if ($this->performanceTracker !== null && $this->performanceTracker->isEnabled()) {
$metrics['processors'] = $this->performanceTracker->generateReport()->toArray();
}
return new JsonResult($metrics);
}
/**
* Get health status
*
* Quick health check for monitoring systems.
* Returns 200 if healthy, 503 if unhealthy.
*
* @route GET /api/livecomponents/health
*/
#[Route('/api/livecomponents/health', method: Method::GET)]
public function health(): JsonResult
{
$hasIssues = $this->metricsCollector->hasPerformanceIssues();
$warnings = $this->metricsCollector->getPerformanceWarnings();
$status = [
'status' => $hasIssues ? 'degraded' : 'healthy',
'components' => [
'registry' => $this->isRegistryHealthy(),
'cache' => ! $hasIssues,
],
'warnings' => $warnings,
'timestamp' => time(),
];
// Return 503 if unhealthy for monitoring systems
$httpStatus = $hasIssues ? 503 : 200;
return new JsonResult($status, $httpStatus);
}
/**
* Get cache performance summary
*
* Focused metrics for cache system performance.
*
* @route GET /api/livecomponents/metrics/cache
*/
#[Route('/api/livecomponents/metrics/cache', method: Method::GET)]
#[Auth(roles: ['admin'])]
public function cacheMetrics(): JsonResult
{
return new JsonResult($this->getCacheMetrics());
}
/**
* Get registry statistics
*
* Component registry stats and metadata.
*
* @route GET /api/livecomponents/metrics/registry
*/
#[Route('/api/livecomponents/metrics/registry', method: Method::GET)]
#[Auth(roles: ['admin'])]
public function registryMetrics(): JsonResult
{
return new JsonResult($this->getRegistryStats());
}
/**
* Reset metrics (development only)
*
* Resets all collected metrics. Only available in development.
*
* @route POST /api/livecomponents/metrics/reset
*/
#[Route('/api/livecomponents/metrics/reset', method: Method::POST)]
#[Auth(roles: ['admin'])]
public function resetMetrics(): JsonResult
{
// Only allow in development
if (getenv('APP_ENV') !== 'development') {
return new JsonResult(
['error' => 'Metric reset only available in development'],
403
);
}
$this->metricsCollector->reset();
if ($this->performanceTracker !== null) {
$this->performanceTracker->reset();
}
return new JsonResult([
'message' => 'Metrics reset successfully',
'timestamp' => time(),
]);
}
/**
* Get cache metrics data
*/
private function getCacheMetrics(): array
{
return $this->metricsCollector->getSummary();
}
/**
* Get registry statistics
*/
private function getRegistryStats(): array
{
$componentNames = $this->registry->getAvailableComponentNames();
return [
'total_components' => count($componentNames),
'component_names' => $componentNames,
'memory_estimate' => $this->estimateRegistryMemoryUsage(),
];
}
/**
* Check if registry is healthy
*/
private function isRegistryHealthy(): bool
{
try {
$components = $this->registry->getAvailableComponentNames();
return ! empty($components);
} catch (\Throwable $e) {
return false;
}
}
/**
* Estimate registry memory usage
*/
private function estimateRegistryMemoryUsage(): int
{
// Rough estimate: ~5KB per component
$componentCount = count($this->registry->getAvailableComponentNames());
return $componentCount * 5 * 1024; // bytes
}
/**
* Inspect specific component
*
* Returns detailed information about a specific component instance
* for debugging and development purposes.
*
* @route GET /api/livecomponents/inspect/{componentId}
*/
#[Route('/api/livecomponents/inspect/{componentId}', method: Method::GET)]
#[Auth(roles: ['admin'])]
public function inspectComponent(string $componentId): JsonResult
{
try {
// Parse component ID
$id = ComponentId::fromString($componentId);
[$componentName, $instanceId] = explode(':', $componentId, 2);
// Get component class name
$className = $this->registry->getClassName($componentName);
if ($className === null) {
return new JsonResult([
'error' => 'Component not found',
'component_id' => $componentId,
], 404);
}
// Get metadata
$metadata = $this->metadataCache->get($className);
// Try to get cached state
$cachedState = $this->stateCache->get($id);
// Build inspection data
$inspection = [
'component' => [
'id' => $componentId,
'name' => $componentName,
'instance_id' => $instanceId,
'class' => $className,
],
'metadata' => [
'properties' => array_map(
fn ($prop) => [
'name' => $prop->name,
'type' => $prop->type,
'nullable' => $prop->nullable,
'hasDefault' => $prop->hasDefaultValue,
],
$metadata->properties
),
'actions' => array_map(
fn ($action) => [
'name' => $action->name,
'parameters' => $action->parameters,
],
$metadata->actions
),
'constructor_params' => $metadata->constructorParams,
'compiled_at' => date('Y-m-d H:i:s', $metadata->compiledAt),
],
'state' => $cachedState !== null ? [
'cached' => true,
'data' => $cachedState->toArray(),
] : [
'cached' => false,
'message' => 'No cached state found',
],
'cache_info' => [
'metadata_cached' => $this->metadataCache->has($className),
'state_cached' => $cachedState !== null,
],
'timestamp' => time(),
];
return new JsonResult($inspection);
} catch (\Throwable $e) {
return new JsonResult([
'error' => 'Inspection failed',
'message' => $e->getMessage(),
'component_id' => $componentId,
], 500);
}
}
/**
* List all component instances
*
* Returns a list of all active component instances with their IDs.
* Useful for discovering what components to inspect.
*
* @route GET /api/livecomponents/instances
*/
#[Route('/api/livecomponents/instances', method: Method::GET)]
#[Auth(roles: ['admin'])]
public function listInstances(): JsonResult
{
$componentNames = $this->registry->getAvailableComponentNames();
$instances = [];
foreach ($componentNames as $name) {
$className = $this->registry->getClassName($name);
if ($className !== null) {
$instances[] = [
'name' => $name,
'class' => $className,
'metadata_cached' => $this->metadataCache->has($className),
];
}
}
return new JsonResult([
'total' => count($instances),
'instances' => $instances,
'timestamp' => time(),
]);
}
}