- 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.
373 lines
12 KiB
PHP
373 lines
12 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use App\Framework\Core\ValueObjects\Duration;
|
|
use App\Framework\Filesystem\ValueObjects\FilePath;
|
|
use App\Framework\Http\Url\Rfc3986Url;
|
|
use App\Framework\Logging\ChannelLogger;
|
|
use App\Framework\Logging\LogChannel;
|
|
use App\Framework\Logging\Logger;
|
|
use App\Framework\Logging\LogLevel;
|
|
use App\Framework\Logging\ValueObjects\LogContext;
|
|
use App\Framework\Process\Services\BackupVerificationService;
|
|
use App\Framework\Process\Services\LogAnalysisService;
|
|
use App\Framework\Process\Services\NetworkDiagnosticsService;
|
|
use App\Framework\Process\Services\SystemHealthCheckService;
|
|
use App\Framework\Process\Services\SystemInfoService;
|
|
use App\Framework\Process\Services\UrlHealthCheckService;
|
|
use App\Framework\Process\SystemProcess;
|
|
|
|
// Simple Logger für Tests
|
|
$logger = new class () implements Logger {
|
|
public ChannelLogger $security {
|
|
get => $this->createDummyChannel();
|
|
}
|
|
|
|
public ChannelLogger $cache {
|
|
get => $this->createDummyChannel();
|
|
}
|
|
|
|
public ChannelLogger $database {
|
|
get => $this->createDummyChannel();
|
|
}
|
|
|
|
public ChannelLogger $framework {
|
|
get => $this->createDummyChannel();
|
|
}
|
|
|
|
public ChannelLogger $error {
|
|
get => $this->createDummyChannel();
|
|
}
|
|
|
|
public function emergency(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function alert(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function critical(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function error(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function warning(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function notice(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function info(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function debug(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function log(LogLevel $level, string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function logToChannel(LogChannel $channel, LogLevel $level, string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
private function createDummyChannel(): ChannelLogger
|
|
{
|
|
return new class ($this) implements ChannelLogger {
|
|
public function __construct(private Logger $logger)
|
|
{
|
|
}
|
|
|
|
public function debug(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function info(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function notice(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function warning(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function error(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function critical(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function alert(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
|
|
public function emergency(string $message, ?LogContext $context = null): void
|
|
{
|
|
}
|
|
};
|
|
}
|
|
};
|
|
|
|
$process = new SystemProcess($logger);
|
|
|
|
echo "=== Process Services Test Suite ===\n\n";
|
|
|
|
// Test 1: SystemInfoService
|
|
echo "Test 1: SystemInfoService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$systemInfoService = new SystemInfoService($process);
|
|
$systemInfo = $systemInfoService();
|
|
|
|
echo "✅ SystemInfo retrieved\n";
|
|
echo " Uptime: " . $systemInfo->uptime->uptime->toHumanReadable() . "\n";
|
|
echo " Load: " . $systemInfo->load->oneMinute . ", " . $systemInfo->load->fiveMinutes . ", " . $systemInfo->load->fifteenMinutes . "\n";
|
|
echo " Memory: " . $systemInfo->memory->getUsagePercentage() . "% used\n";
|
|
echo " Disk: " . $systemInfo->disk->getUsagePercentage() . "% used\n";
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test 2: SystemHealthCheckService
|
|
echo "Test 2: SystemHealthCheckService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$systemInfoService = new SystemInfoService($process);
|
|
$healthCheckService = new SystemHealthCheckService($systemInfoService);
|
|
$healthReport = $healthCheckService();
|
|
|
|
echo "✅ Health Check completed\n";
|
|
echo " Overall Status: " . $healthReport->overallStatus->value . "\n";
|
|
echo " Total Checks: " . count($healthReport->checks) . "\n";
|
|
|
|
$counts = $healthReport->getStatusCounts();
|
|
echo " Healthy: " . $counts['healthy'] . "\n";
|
|
echo " Degraded: " . $counts['degraded'] . "\n";
|
|
echo " Unhealthy: " . $counts['unhealthy'] . "\n";
|
|
|
|
echo "\n Check Details:\n";
|
|
foreach ($healthReport->checks as $check) {
|
|
$icon = match($check->status) {
|
|
\App\Framework\Process\ValueObjects\Health\HealthStatus::HEALTHY => '✅',
|
|
\App\Framework\Process\ValueObjects\Health\HealthStatus::DEGRADED => '⚠️',
|
|
\App\Framework\Process\ValueObjects\Health\HealthStatus::UNHEALTHY => '❌',
|
|
};
|
|
echo " {$icon} {$check->name}: {$check->message}\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test 3: LogAnalysisService
|
|
echo "Test 3: LogAnalysisService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$logAnalysisService = new LogAnalysisService($process);
|
|
|
|
// Test mit /var/log/syslog falls vorhanden
|
|
$logFile = FilePath::create('/var/log/syslog');
|
|
|
|
if ($logFile->exists()) {
|
|
$result = $logAnalysisService->findErrors($logFile, 100);
|
|
|
|
echo "✅ Log Analysis completed\n";
|
|
echo " Total Entries: " . $result->getTotalCount() . "\n";
|
|
echo " Errors: " . count($result->getErrors()) . "\n";
|
|
echo " Warnings: " . count($result->getWarnings()) . "\n";
|
|
echo " Info: " . count($result->getInfoMessages()) . "\n";
|
|
|
|
if (! empty($result->getErrors())) {
|
|
echo "\n Top Errors:\n";
|
|
$topErrors = $result->getTopErrors(3);
|
|
foreach ($topErrors as $message => $count) {
|
|
echo " - [{$count}x] " . substr($message, 0, 60) . "...\n";
|
|
}
|
|
}
|
|
} else {
|
|
echo "⏭️ Skipped: /var/log/syslog not found\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test 4: BackupVerificationService
|
|
echo "Test 4: BackupVerificationService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$backupService = new BackupVerificationService($process);
|
|
|
|
// Test mit temporärem Verzeichnis
|
|
$tmpDir = FilePath::create(sys_get_temp_dir() . '/test-backups');
|
|
|
|
if (! $tmpDir->exists()) {
|
|
mkdir($tmpDir->toString());
|
|
}
|
|
|
|
// Erstelle Test-Backup-Datei
|
|
$testBackup = $tmpDir->toString() . '/backup-test.sql';
|
|
file_put_contents($testBackup, "-- Test SQL Backup\nSELECT * FROM test;");
|
|
|
|
$result = $backupService->verify($tmpDir, '*.sql');
|
|
|
|
echo "✅ Backup Verification completed\n";
|
|
echo " Total Backups: " . $result->totalCount . "\n";
|
|
echo " Fresh Backups: " . $result->getFreshBackupCount() . "\n";
|
|
echo " Old Backups: " . $result->getOldBackupCount() . "\n";
|
|
|
|
if ($latest = $result->getLatestBackup()) {
|
|
echo " Latest Backup: " . $latest->name . "\n";
|
|
echo " Created: " . $latest->createdAt->format('Y-m-d H:i:s') . "\n";
|
|
echo " Size: " . $latest->size->toHumanReadable() . "\n";
|
|
}
|
|
|
|
// Cleanup
|
|
unlink($testBackup);
|
|
rmdir($tmpDir->toString());
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test 5: NetworkDiagnosticsService
|
|
echo "Test 5: NetworkDiagnosticsService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$networkService = new NetworkDiagnosticsService($process);
|
|
|
|
// Ping Test
|
|
echo " Ping Test:\n";
|
|
|
|
try {
|
|
$pingResult = $networkService->ping('8.8.8.8', 2);
|
|
|
|
if ($pingResult->isReachable) {
|
|
echo " ✅ 8.8.8.8 is reachable\n";
|
|
echo " Latency: " . $pingResult->latency->toHumanReadable() . "\n";
|
|
echo " Packet Loss: " . $pingResult->packetLoss . "%\n";
|
|
} else {
|
|
echo " ❌ 8.8.8.8 is not reachable\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo " ⏭️ Ping skipped: " . $e->getMessage() . "\n";
|
|
}
|
|
|
|
// DNS Lookup Test
|
|
echo "\n DNS Lookup Test:\n";
|
|
|
|
try {
|
|
$dnsResult = $networkService->dnsLookup('google.com');
|
|
|
|
if ($dnsResult->resolved) {
|
|
echo " ✅ google.com resolved\n";
|
|
echo " Addresses: " . count($dnsResult->addresses) . "\n";
|
|
echo " IPv4 Count: " . count($dnsResult->getIpv4Addresses()) . "\n";
|
|
echo " IPv6 Count: " . count($dnsResult->getIpv6Addresses()) . "\n";
|
|
|
|
if ($firstAddress = $dnsResult->getFirstAddress()) {
|
|
echo " First Address: " . $firstAddress->value . "\n";
|
|
}
|
|
} else {
|
|
echo " ❌ google.com failed to resolve\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo " ⏭️ DNS lookup skipped: " . $e->getMessage() . "\n";
|
|
}
|
|
|
|
// Port Check Test
|
|
echo "\n Port Check Test:\n";
|
|
|
|
try {
|
|
$portResult = $networkService->checkPort('google.com', 443);
|
|
|
|
if ($portResult->isOpen) {
|
|
echo " ✅ Port 443 is open on google.com\n";
|
|
echo " Service: " . $portResult->service . "\n";
|
|
} else {
|
|
echo " ❌ Port 443 is closed on google.com\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo " ⏭️ Port check skipped: " . $e->getMessage() . "\n";
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
// Test 6: UrlHealthCheckService
|
|
echo "Test 6: UrlHealthCheckService\n";
|
|
echo str_repeat('-', 50) . "\n";
|
|
|
|
try {
|
|
$urlHealthService = new UrlHealthCheckService($process);
|
|
|
|
$url = Rfc3986Url::parse('https://www.google.com');
|
|
$timeout = Duration::fromSeconds(5);
|
|
|
|
$healthCheck = $urlHealthService->checkUrl($url, $timeout);
|
|
|
|
if ($healthCheck->isAccessible) {
|
|
echo "✅ URL is accessible\n";
|
|
echo " Status: " . $healthCheck->status->value . " - " . $healthCheck->status->getDescription() . "\n";
|
|
echo " Response Time: " . $healthCheck->responseTime->toHumanReadable() . "\n";
|
|
echo " Is Successful: " . ($healthCheck->isSuccessful() ? 'Yes' : 'No') . "\n";
|
|
echo " Is Redirect: " . ($healthCheck->isRedirect() ? 'Yes' : 'No') . "\n";
|
|
echo " Fast Response (<1s): " . ($healthCheck->isResponseTimeFast(Duration::fromSeconds(1)) ? 'Yes' : 'No') . "\n";
|
|
|
|
if ($healthCheck->redirectUrl) {
|
|
echo " Redirect URL: " . $healthCheck->redirectUrl->toString() . "\n";
|
|
}
|
|
} else {
|
|
echo "❌ URL is not accessible\n";
|
|
echo " Error: " . $healthCheck->error . "\n";
|
|
}
|
|
|
|
// Test mit Headers
|
|
echo "\n Testing with Headers:\n";
|
|
$healthCheckWithHeaders = $urlHealthService->checkUrlWithHeaders($url, $timeout);
|
|
|
|
if ($healthCheckWithHeaders->isAccessible && $healthCheckWithHeaders->headers) {
|
|
echo " ✅ Headers retrieved\n";
|
|
|
|
$headers = $healthCheckWithHeaders->headers->all();
|
|
$headerCount = count($headers);
|
|
echo " Total Headers: {$headerCount}\n";
|
|
|
|
if ($contentType = $healthCheckWithHeaders->headers->getFirst('Content-Type')) {
|
|
echo " Content-Type: {$contentType}\n";
|
|
}
|
|
|
|
if ($server = $healthCheckWithHeaders->headers->getFirst('Server')) {
|
|
echo " Server: {$server}\n";
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
echo "❌ Failed: " . $e->getMessage() . "\n";
|
|
}
|
|
echo "\n";
|
|
|
|
echo "=== Test Suite Completed ===\n";
|