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

@@ -18,9 +18,11 @@ beforeEach(function () {
it('only handles records in CLI mode', function () {
// ConsoleHandler should only work in CLI mode
expect(PHP_SAPI)->toBe('cli');
$cliCheck = PHP_SAPI === 'cli';
expect($cliCheck)->toBe(true);
$handler = new ConsoleHandler();
// Create handler with debugOnly = false to avoid APP_DEBUG dependency
$handler = new ConsoleHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test message',
@@ -30,11 +32,12 @@ it('only handles records in CLI mode', function () {
);
// In CLI mode, should handle the record
expect($handler->isHandling($record))->toBeTrue();
$result = $handler->isHandling($record);
expect($result)->toBe(true);
});
it('respects minimum level configuration', function () {
$handler = new ConsoleHandler(minLevel: LogLevel::WARNING);
$handler = new ConsoleHandler(minLevel: LogLevel::WARNING, debugOnly: false);
$debugRecord = new LogRecord(
message: 'Debug',
@@ -64,10 +67,17 @@ it('respects minimum level configuration', function () {
timestamp: $this->timestamp
);
expect($handler->isHandling($debugRecord))->toBeFalse();
expect($handler->isHandling($infoRecord))->toBeFalse();
expect($handler->isHandling($warningRecord))->toBeTrue();
expect($handler->isHandling($errorRecord))->toBeTrue();
$debugResult = $handler->isHandling($debugRecord);
expect($debugResult)->toBe(false);
$infoResult = $handler->isHandling($infoRecord);
expect($infoResult)->toBe(false);
$warningResult = $handler->isHandling($warningRecord);
expect($warningResult)->toBe(true);
$errorResult = $handler->isHandling($errorRecord);
expect($errorResult)->toBe(true);
});
it('respects debug only mode when APP_DEBUG is not set', function () {
@@ -85,16 +95,19 @@ it('respects debug only mode when APP_DEBUG is not set', function () {
timestamp: $this->timestamp
);
expect($handler->isHandling($record))->toBeFalse();
$result1 = $handler->isHandling($record);
expect($result1)->toBe(false);
// Test with APP_DEBUG = true
putenv('APP_DEBUG=true');
expect($handler->isHandling($record))->toBeTrue();
$result2 = $handler->isHandling($record);
expect($result2)->toBe(true);
// Test with debugOnly = false (should always handle)
putenv('APP_DEBUG=false');
$handler = new ConsoleHandler(debugOnly: false);
expect($handler->isHandling($record))->toBeTrue();
$result3 = $handler->isHandling($record);
expect($result3)->toBe(true);
// Restore original value
if ($originalDebug !== false) {
@@ -105,7 +118,7 @@ it('respects debug only mode when APP_DEBUG is not set', function () {
});
it('can change minimum level after creation', function () {
$handler = new ConsoleHandler(minLevel: LogLevel::DEBUG);
$handler = new ConsoleHandler(minLevel: LogLevel::DEBUG, debugOnly: false);
$infoRecord = new LogRecord(
message: 'Info',
@@ -114,15 +127,17 @@ it('can change minimum level after creation', function () {
timestamp: $this->timestamp
);
expect($handler->isHandling($infoRecord))->toBeTrue();
$result1 = $handler->isHandling($infoRecord);
expect($result1)->toBe(true);
$handler->setMinLevel(LogLevel::WARNING);
expect($handler->isHandling($infoRecord))->toBeFalse();
$result2 = $handler->isHandling($infoRecord);
expect($result2)->toBe(false);
});
it('can change output format', function () {
$handler = new ConsoleHandler();
$handler = new ConsoleHandler(debugOnly: false);
$originalFormat = '{color}[{level_name}]{reset} {timestamp} {request_id}{message}{structured}';
$newFormat = '{level_name}: {message}';
@@ -131,15 +146,12 @@ it('can change output format', function () {
// Note: We can't easily test the actual output without mocking file_put_contents or echo,
// but we can verify the method returns the handler for fluent interface
expect($handler->setOutputFormat($newFormat))->toBe($handler);
$result = $handler->setOutputFormat($newFormat);
expect($result)->toBe($handler);
});
it('handles output correctly using stdout and stderr', function () {
// Save original APP_DEBUG value
$originalDebug = getenv('APP_DEBUG');
putenv('APP_DEBUG=true');
$handler = new ConsoleHandler(stderrLevel: LogLevel::WARNING);
$handler = new ConsoleHandler(stderrLevel: LogLevel::WARNING, debugOnly: false);
// Test that lower levels would go to stdout (DEBUG, INFO, NOTICE)
$infoRecord = new LogRecord(
@@ -158,8 +170,11 @@ it('handles output correctly using stdout and stderr', function () {
);
// We can verify the handler processes these records
expect($handler->isHandling($infoRecord))->toBeTrue();
expect($handler->isHandling($errorRecord))->toBeTrue();
$result1 = $handler->isHandling($infoRecord);
expect($result1)->toBe(true);
$result2 = $handler->isHandling($errorRecord);
expect($result2)->toBe(true);
// Capture output
ob_start();
@@ -173,23 +188,15 @@ it('handles output correctly using stdout and stderr', function () {
$handler->handle($errorRecord);
// The handler should have processed both records
expect($handler->isHandling($infoRecord))->toBeTrue();
expect($handler->isHandling($errorRecord))->toBeTrue();
$result3 = $handler->isHandling($infoRecord);
expect($result3)->toBe(true);
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
$result4 = $handler->isHandling($errorRecord);
expect($result4)->toBe(true);
});
it('formats records with extra data correctly', function () {
// Save original APP_DEBUG value
$originalDebug = getenv('APP_DEBUG');
putenv('APP_DEBUG=true');
$handler = new ConsoleHandler();
$handler = new ConsoleHandler(debugOnly: false);
$record = new LogRecord(
message: 'Test with extras',
@@ -199,23 +206,24 @@ it('formats records with extra data correctly', function () {
);
// Add various types of extra data
$record->addExtra('request_id', 'req-123');
$record->addExtra('structured_tags', ['important', 'audit']);
$record->addExtra('trace_context', [
'trace_id' => 'trace-abc-def-123',
'active_span' => ['spanId' => 'span-456-789'],
]);
$record->addExtra('user_context', [
'user_id' => 'user-999',
'is_authenticated' => true,
]);
$record->addExtra('request_context', [
'request_method' => 'POST',
'request_uri' => '/api/users/create',
]);
$record = $record->withExtra('request_id', 'req-123')
->withExtra('structured_tags', ['important', 'audit'])
->withExtra('trace_context', [
'trace_id' => 'trace-abc-def-123',
'active_span' => ['spanId' => 'span-456-789'],
])
->withExtra('user_context', [
'user_id' => 'user-999',
'is_authenticated' => true,
])
->withExtra('request_context', [
'request_method' => 'POST',
'request_uri' => '/api/users/create',
]);
// The handler should process this record
expect($handler->isHandling($record))->toBeTrue();
$result = $handler->isHandling($record);
expect($result)->toBe(true);
// Capture the output
ob_start();
@@ -226,22 +234,12 @@ it('formats records with extra data correctly', function () {
expect($output)->toContain('Test with extras');
// It should contain the request_id
expect($output)->toContain('req-123');
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
});
it('handles records with channel information', function () {
// Save original APP_DEBUG value
$originalDebug = getenv('APP_DEBUG');
putenv('APP_DEBUG=true');
$handler = new ConsoleHandler(
outputFormat: '{channel}{level_name}: {message}'
outputFormat: '{channel}{level_name}: {message}',
debugOnly: false
);
$record = new LogRecord(
@@ -252,7 +250,8 @@ it('handles records with channel information', function () {
channel: 'database'
);
expect($handler->isHandling($record))->toBeTrue();
$result = $handler->isHandling($record);
expect($result)->toBe(true);
// Capture output
ob_start();
@@ -262,31 +261,17 @@ it('handles records with channel information', function () {
// The output should contain the channel
expect($output)->toContain('[database]');
expect($output)->toContain('Database connection established');
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
});
it('applies correct colors for different log levels', function () {
// Save original APP_DEBUG value
$originalDebug = getenv('APP_DEBUG');
putenv('APP_DEBUG=true');
$handler = new ConsoleHandler();
it('applies correct colors for stdout log levels', function () {
$handler = new ConsoleHandler(debugOnly: false);
// Only test stdout levels (DEBUG, INFO, NOTICE)
// WARNING and above go to stderr and cannot be captured with ob_start()
$levels = [
LogLevel::DEBUG,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
LogLevel::ERROR,
LogLevel::CRITICAL,
LogLevel::ALERT,
LogLevel::EMERGENCY,
];
foreach ($levels as $level) {
@@ -306,11 +291,4 @@ it('applies correct colors for different log levels', function () {
expect($output)->toContain($expectedColor);
expect($output)->toContain("{$level->getName()} message");
}
// Restore original value
if ($originalDebug !== false) {
putenv("APP_DEBUG={$originalDebug}");
} else {
putenv('APP_DEBUG');
}
});