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.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -0,0 +1,365 @@
<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents\Observability;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Metrics\Metric;
use App\Framework\Metrics\MetricType;
use App\Framework\Performance\PerformanceCategory;
use App\Framework\Performance\PerformanceCollector;
/**
* Component Metrics Collector
*
* Spezialisierte Metrics für LiveComponents:
* - Render-Zeiten (hydrate_ms, render_ms, action_ms)
* - Cache-Effizienz (cache_hit_rate, cache_miss_total)
* - Action-Performance (action_latency_ms, action_error_rate)
* - Event-Tracking (events_dispatched_total, events_received_total)
* - Upload-Performance (upload_chunk_duration_ms, upload_success_rate)
*/
final class ComponentMetricsCollector
{
/** @var array<string, Metric> */
private array $metrics = [];
public function __construct(
private readonly ?PerformanceCollector $performanceCollector = null
) {
}
/**
* Record component render time
*/
public function recordRender(string $componentId, float $durationMs, bool $cached = false): void
{
$this->increment('livecomponent_renders_total', [
'component_id' => $componentId,
'cached' => $cached ? 'true' : 'false',
]);
$this->recordHistogram(
'livecomponent_render_duration_ms',
$durationMs,
['component_id' => $componentId, 'cached' => $cached ? 'true' : 'false']
);
// Integrate with framework performance collector
if ($this->performanceCollector !== null) {
$this->performanceCollector->recordMetric(
"livecomponent.render.{$componentId}",
PerformanceCategory::RENDERING,
$durationMs,
['cached' => $cached]
);
}
}
/**
* Record action execution time
*/
public function recordAction(
string $componentId,
string $actionName,
float $durationMs,
bool $success = true
): void {
$this->increment('livecomponent_actions_total', [
'component_id' => $componentId,
'action' => $actionName,
'status' => $success ? 'success' : 'error',
]);
$this->recordHistogram(
'livecomponent_action_duration_ms',
$durationMs,
['component_id' => $componentId, 'action' => $actionName]
);
if (!$success) {
$this->increment('livecomponent_action_errors_total', [
'component_id' => $componentId,
'action' => $actionName,
]);
}
// Framework integration
if ($this->performanceCollector !== null) {
$this->performanceCollector->recordMetric(
"livecomponent.action.{$componentId}.{$actionName}",
PerformanceCategory::APPLICATION,
$durationMs,
['success' => $success]
);
}
}
/**
* Record cache hit/miss
*/
public function recordCacheHit(string $componentId, bool $hit): void
{
$metricName = $hit ? 'livecomponent_cache_hits_total' : 'livecomponent_cache_misses_total';
$this->increment($metricName, ['component_id' => $componentId]);
}
/**
* Record event dispatch
*/
public function recordEventDispatched(string $componentId, string $eventName): void
{
$this->increment('livecomponent_events_dispatched_total', [
'component_id' => $componentId,
'event' => $eventName,
]);
}
/**
* Record event received
*/
public function recordEventReceived(string $componentId, string $eventName): void
{
$this->increment('livecomponent_events_received_total', [
'component_id' => $componentId,
'event' => $eventName,
]);
}
/**
* Record hydration time
*/
public function recordHydration(string $componentId, float $durationMs): void
{
$this->recordHistogram(
'livecomponent_hydration_duration_ms',
$durationMs,
['component_id' => $componentId]
);
}
/**
* Record batch operation
*/
public function recordBatch(int $operationCount, float $durationMs, int $successCount, int $failureCount): void
{
$this->increment('livecomponent_batch_operations_total', ['status' => 'executed']);
$this->recordHistogram('livecomponent_batch_size', (float) $operationCount);
$this->recordHistogram('livecomponent_batch_duration_ms', $durationMs);
if ($successCount > 0) {
$this->increment('livecomponent_batch_success_total', [], $successCount);
}
if ($failureCount > 0) {
$this->increment('livecomponent_batch_failure_total', [], $failureCount);
}
}
/**
* Record fragment update
*/
public function recordFragmentUpdate(string $componentId, int $fragmentCount, float $durationMs): void
{
$this->increment('livecomponent_fragment_updates_total', [
'component_id' => $componentId,
]);
$this->recordHistogram('livecomponent_fragment_count', (float) $fragmentCount);
$this->recordHistogram('livecomponent_fragment_duration_ms', $durationMs);
}
/**
* Record upload chunk
*/
public function recordUploadChunk(
string $sessionId,
int $chunkIndex,
float $durationMs,
bool $success = true
): void {
$this->increment('livecomponent_upload_chunks_total', [
'session_id' => $sessionId,
'status' => $success ? 'success' : 'error',
]);
$this->recordHistogram(
'livecomponent_upload_chunk_duration_ms',
$durationMs,
['session_id' => $sessionId]
);
}
/**
* Record upload completion
*/
public function recordUploadComplete(string $sessionId, float $totalDurationMs, int $totalChunks): void
{
$this->increment('livecomponent_uploads_completed_total', ['session_id' => $sessionId]);
$this->recordHistogram('livecomponent_upload_total_duration_ms', $totalDurationMs);
$this->recordHistogram('livecomponent_upload_chunk_count', (float) $totalChunks);
}
/**
* Get all metrics
*
* @return array<string, Metric>
*/
public function getMetrics(): array
{
return $this->metrics;
}
/**
* Get metric by name
*/
public function getMetric(string $name): ?Metric
{
return $this->metrics[$name] ?? null;
}
/**
* Get metrics summary for monitoring/dashboard
*/
public function getSummary(): array
{
$summary = [
'total_renders' => 0,
'total_actions' => 0,
'cache_hits' => 0,
'cache_misses' => 0,
'total_events' => 0,
'action_errors' => 0,
'avg_render_time_ms' => 0.0,
'avg_action_time_ms' => 0.0,
'cache_hit_rate' => 0.0,
];
// Calculate totals
foreach ($this->metrics as $metric) {
if (str_contains($metric->name, 'renders_total')) {
$summary['total_renders'] += (int) $metric->value;
} elseif (str_contains($metric->name, 'actions_total')) {
$summary['total_actions'] += (int) $metric->value;
} elseif (str_contains($metric->name, 'cache_hits_total')) {
$summary['cache_hits'] += (int) $metric->value;
} elseif (str_contains($metric->name, 'cache_misses_total')) {
$summary['cache_misses'] += (int) $metric->value;
} elseif (str_contains($metric->name, 'events_dispatched_total')) {
$summary['total_events'] += (int) $metric->value;
} elseif (str_contains($metric->name, 'action_errors_total')) {
$summary['action_errors'] += (int) $metric->value;
}
}
// Calculate cache hit rate
$totalCacheAccess = $summary['cache_hits'] + $summary['cache_misses'];
if ($totalCacheAccess > 0) {
$summary['cache_hit_rate'] = ($summary['cache_hits'] / $totalCacheAccess) * 100;
}
return $summary;
}
/**
* Export metrics in Prometheus format
*/
public function exportPrometheus(): string
{
$output = "# HELP LiveComponents metrics\n";
$output .= "# TYPE livecomponent_* counter/histogram\n\n";
foreach ($this->metrics as $metric) {
$labels = $metric->getFormattedLabels();
$timestamp = $metric->timestamp?->toTimestamp() ?? time();
$output .= sprintf(
"%s%s %.2f %d\n",
$metric->name,
$labels,
$metric->value,
$timestamp
);
}
return $output;
}
/**
* Reset all metrics
*/
public function reset(): void
{
$this->metrics = [];
}
/**
* Increment counter metric
*/
private function increment(string $name, array $labels = [], int $amount = 1): void
{
$key = $this->buildMetricKey($name, $labels);
if (!isset($this->metrics[$key])) {
$this->metrics[$key] = new Metric(
name: $name,
value: 0,
type: MetricType::COUNTER,
labels: $labels,
timestamp: Timestamp::now()
);
}
$currentValue = $this->metrics[$key]->value;
$this->metrics[$key] = $this->metrics[$key]
->withValue($currentValue + $amount)
->withTimestamp(Timestamp::now());
}
/**
* Record histogram metric
*/
private function recordHistogram(string $name, float $value, array $labels = []): void
{
$key = $this->buildMetricKey($name, $labels);
if (!isset($this->metrics[$key])) {
$this->metrics[$key] = new Metric(
name: $name,
value: $value,
type: MetricType::HISTOGRAM,
labels: $labels,
unit: 'ms',
timestamp: Timestamp::now()
);
} else {
// For histograms, we keep updating with new observations
$this->metrics[$key] = $this->metrics[$key]
->withValue($value)
->withTimestamp(Timestamp::now());
}
}
/**
* Build unique metric key from name and labels
*/
private function buildMetricKey(string $name, array $labels): string
{
if (empty($labels)) {
return $name;
}
ksort($labels);
$labelString = implode(',', array_map(
fn($key, $value) => "{$key}={$value}",
array_keys($labels),
$labels
));
return "{$name}{{$labelString}}";
}
}