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,120 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Framework\Logging;
use App\Framework\Logging\Handlers\SamplingLogHandler;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\Sampling\SamplingConfig;
use App\Framework\Logging\ValueObjects\LogContext;
use PHPUnit\Framework\TestCase;
final class SamplingLogHandlerTest extends TestCase
{
public function test_samples_logs_based_on_config(): void
{
$inner = $this->createMock(LogHandler::class);
// Config: 100% ERROR, 0% INFO
$config = SamplingConfig::custom([
'ERROR' => 1.0,
'INFO' => 0.0,
]);
$handler = new SamplingLogHandler($inner, $config);
// ERROR sollte durchkommen
$inner->expects($this->once())->method('handle');
$handler->handle($this->createLogRecord('error', LogLevel::ERROR));
// INFO sollte gedroppt werden
$handler->handle($this->createLogRecord('info', LogLevel::INFO));
$this->assertEquals(1, $handler->getAcceptedCount());
$this->assertEquals(1, $handler->getDroppedCount());
}
public function test_never_samples_critical_levels(): void
{
$inner = $this->createMock(LogHandler::class);
$config = SamplingConfig::production();
$handler = new SamplingLogHandler($inner, $config);
$criticalLevels = [
LogLevel::ERROR,
LogLevel::CRITICAL,
LogLevel::ALERT,
LogLevel::EMERGENCY,
];
$inner->expects($this->exactly(count($criticalLevels)))
->method('handle');
foreach ($criticalLevels as $level) {
$handler->handle($this->createLogRecord('test', $level));
}
$this->assertEquals(count($criticalLevels), $handler->getAcceptedCount());
$this->assertEquals(0, $handler->getDroppedCount());
}
public function test_tracks_dropped_by_level(): void
{
$inner = $this->createMock(LogHandler::class);
$config = SamplingConfig::custom(['INFO' => 0.0]);
$handler = new SamplingLogHandler($inner, $config);
for ($i = 0; $i < 10; $i++) {
$handler->handle($this->createLogRecord('info', LogLevel::INFO));
}
$dropped = $handler->getDroppedByLevel();
$this->assertArrayHasKey('INFO', $dropped);
$this->assertEquals(10, $dropped['INFO']);
}
public function test_calculates_drop_rate(): void
{
$inner = $this->createMock(LogHandler::class);
$config = SamplingConfig::custom(['INFO' => 0.5]);
$handler = new SamplingLogHandler($inner, $config);
// Simulate: sollte ca. 50% droppen
// Für deterministische Tests mocken wir mt_rand nicht,
// aber prüfen nur dass Drop-Rate berechnet wird
$this->assertEquals(0.0, $handler->getDropRate()); // Noch keine Logs
}
public function test_health_check(): void
{
$inner = $this->createMock(LogHandler::class);
$handler = new SamplingLogHandler($inner, SamplingConfig::production());
$health = $handler->check();
$this->assertEquals(\App\Framework\Health\HealthStatus::HEALTHY, $health->status);
$this->assertArrayHasKey('accepted', $health->details);
$this->assertArrayHasKey('dropped', $health->details);
}
private function createLogRecord(
string $message = 'test',
LogLevel $level = LogLevel::INFO
): LogRecord {
return new LogRecord(
level: $level,
message: $message,
channel: 'test',
context: LogContext::empty(),
timestamp: new \DateTimeImmutable()
);
}
}