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

@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Framework\ErrorAggregation;
use App\Framework\Exception\Core\ErrorSeverity;
use App\Framework\Exception\ErrorCode;
use App\Framework\Exception\ErrorHandlerContext;
use App\Framework\Ulid\Ulid;
@@ -36,10 +37,10 @@ final readonly class ErrorEvent
/**
* Creates ErrorEvent from ErrorHandlerContext
*/
public static function fromErrorHandlerContext(ErrorHandlerContext $context): self
public static function fromErrorHandlerContext(ErrorHandlerContext $context, \App\Framework\DateTime\Clock $clock): self
{
return new self(
id: Ulid::generate(),
id: new Ulid($clock),
service: self::extractServiceName($context),
component: $context->exception->component ?? 'unknown',
operation: $context->exception->operation ?? 'unknown',
@@ -64,7 +65,7 @@ final readonly class ErrorEvent
public function toArray(): array
{
return [
'id' => $this->id->toString(),
'id' => (string) $this->id,
'service' => $this->service,
'component' => $this->component,
'operation' => $this->operation,
@@ -169,12 +170,14 @@ final readonly class ErrorEvent
// Try to extract service from request URI
$uri = $context->request->requestUri;
if (str_starts_with($uri, '/api/')) {
return 'api';
}
if ($uri !== null) {
if (str_starts_with($uri, '/api/')) {
return 'api';
}
if (str_starts_with($uri, '/admin/')) {
return 'admin';
if (str_starts_with($uri, '/admin/')) {
return 'admin';
}
}
// Extract from component if available
@@ -187,27 +190,19 @@ final readonly class ErrorEvent
private static function extractErrorCode(ErrorHandlerContext $context): ErrorCode
{
// Try to get from exception metadata
if (isset($context->exception->metadata['error_code'])) {
return ErrorCode::from($context->exception->metadata['error_code']);
// Try to get ErrorCode from original exception if it's a FrameworkException
if (isset($context->exception->data['original_exception'])) {
$originalException = $context->exception->data['original_exception'];
if ($originalException instanceof \App\Framework\Exception\FrameworkException) {
$errorCode = $originalException->getErrorCode();
if ($errorCode !== null) {
return $errorCode;
}
}
}
// Try to get from exception data
if (isset($context->exception->data['error_code'])) {
return ErrorCode::from($context->exception->data['error_code']);
}
// Fallback based on HTTP status
$httpStatus = $context->metadata['http_status'] ?? 500;
return match (true) {
$httpStatus >= 500 => ErrorCode::SYSTEM_RESOURCE_EXHAUSTED,
$httpStatus === 404 => ErrorCode::HTTP_NOT_FOUND,
$httpStatus === 401 => ErrorCode::AUTH_CREDENTIALS_INVALID,
$httpStatus === 403 => ErrorCode::AUTH_INSUFFICIENT_PRIVILEGES,
$httpStatus === 429 => ErrorCode::HTTP_RATE_LIMIT_EXCEEDED,
default => ErrorCode::SYSTEM_RESOURCE_EXHAUSTED,
};
// Fallback: Use SystemErrorCode::RESOURCE_EXHAUSTED as generic error
return \App\Framework\Exception\Core\SystemErrorCode::RESOURCE_EXHAUSTED;
}
private static function extractUserMessage(ErrorHandlerContext $context): string
@@ -217,11 +212,22 @@ final readonly class ErrorEvent
return $context->exception->data['user_message'];
}
// Try exception_message
// Try exception_message (stored by ErrorHandler)
if (isset($context->exception->data['exception_message'])) {
return $context->exception->data['exception_message'];
}
// Try to get from original_exception if it's a FrameworkException
if (isset($context->exception->data['original_exception'])) {
$originalException = $context->exception->data['original_exception'];
if ($originalException instanceof \App\Framework\Exception\FrameworkException) {
return $originalException->getMessage();
}
if ($originalException instanceof \Throwable) {
return $originalException->getMessage();
}
}
// Fallback to operation and component
$operation = $context->exception->operation ?? 'unknown_operation';
$component = $context->exception->component ?? 'unknown_component';
@@ -241,6 +247,12 @@ final readonly class ErrorEvent
return ErrorSeverity::tryFrom($context->exception->metadata['severity']) ?? ErrorSeverity::ERROR;
}
// Get severity from ErrorCode if available
$errorCode = self::extractErrorCode($context);
if ($errorCode !== null) {
return $errorCode->getSeverity();
}
// Determine from HTTP status
$httpStatus = $context->metadata['http_status'] ?? 500;