- 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.
203 lines
6.3 KiB
PHP
203 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../vendor/autoload.php';
|
|
|
|
use App\Framework\Cache\Cache;
|
|
use App\Framework\Cache\CacheIdentifier;
|
|
use App\Framework\Cache\CacheKey;
|
|
use App\Framework\Cache\CacheItem;
|
|
use App\Framework\Cache\CacheResult;
|
|
use App\Framework\Core\ValueObjects\Duration;
|
|
use App\Framework\DateTime\SystemClock;
|
|
use App\Framework\ErrorAggregation\ErrorAggregator;
|
|
use App\Framework\ErrorAggregation\Storage\InMemoryErrorStorage;
|
|
use App\Framework\ErrorHandling\ErrorHandler;
|
|
use App\Framework\ErrorReporting\ErrorReporter;
|
|
use App\Framework\ErrorReporting\Storage\InMemoryErrorReportStorage;
|
|
use App\Framework\Exception\Core\DatabaseErrorCode;
|
|
use App\Framework\Exception\FrameworkException;
|
|
use App\Framework\Http\RequestIdGenerator;
|
|
use App\Framework\Http\ResponseEmitter;
|
|
use App\Framework\DI\DefaultContainer;
|
|
use App\Framework\Queue\InMemoryQueue;
|
|
|
|
// Create all dependencies
|
|
$container = new DefaultContainer();
|
|
$emitter = new ResponseEmitter();
|
|
$requestIdGenerator = new RequestIdGenerator();
|
|
|
|
// Error Aggregation setup
|
|
$errorStorage = new InMemoryErrorStorage();
|
|
$cache = new class implements Cache {
|
|
private array $data = [];
|
|
|
|
public function get(CacheIdentifier ...$identifiers): CacheResult
|
|
{
|
|
$items = [];
|
|
foreach ($identifiers as $identifier) {
|
|
$keyStr = $identifier->toString();
|
|
if (isset($this->data[$keyStr])) {
|
|
$items[] = $this->data[$identifier->toString()];
|
|
} else {
|
|
$items[] = CacheItem::miss($identifier instanceof CacheKey ? $identifier : CacheKey::fromString($identifier->toString()));
|
|
}
|
|
}
|
|
|
|
return count($items) === 1
|
|
? CacheResult::single($items[0])
|
|
: CacheResult::multiple($items);
|
|
}
|
|
|
|
public function set(CacheItem ...$items): bool
|
|
{
|
|
foreach ($items as $item) {
|
|
$this->data[$item->key->toString()] = $item;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function has(CacheIdentifier ...$identifiers): array
|
|
{
|
|
$result = [];
|
|
foreach ($identifiers as $identifier) {
|
|
$result[] = isset($this->data[$identifier->toString()]);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function forget(CacheIdentifier ...$identifiers): bool
|
|
{
|
|
foreach ($identifiers as $identifier) {
|
|
unset($this->data[$identifier->toString()]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function clear(): bool
|
|
{
|
|
$this->data = [];
|
|
return true;
|
|
}
|
|
|
|
public function remember(CacheKey $key, callable $callback, ?Duration $ttl = null): CacheItem
|
|
{
|
|
$keyStr = $key->toString();
|
|
if (isset($this->data[$keyStr])) {
|
|
return $this->data[$keyStr];
|
|
}
|
|
|
|
$value = $callback();
|
|
$item = $ttl ? CacheItem::forSetting($key, $value, $ttl) : CacheItem::miss($key);
|
|
$this->data[$keyStr] = $item;
|
|
return $item;
|
|
}
|
|
};
|
|
$clock = new SystemClock();
|
|
$alertQueue = new InMemoryQueue();
|
|
|
|
$errorAggregator = new ErrorAggregator(
|
|
storage: $errorStorage,
|
|
cache: $cache,
|
|
clock: $clock,
|
|
alertQueue: $alertQueue,
|
|
logger: null,
|
|
batchSize: 100,
|
|
maxRetentionDays: 90
|
|
);
|
|
|
|
// Error Reporting setup
|
|
$errorReportStorage = new InMemoryErrorReportStorage();
|
|
$reportQueue = new InMemoryQueue();
|
|
|
|
$errorReporter = new ErrorReporter(
|
|
storage: $errorReportStorage,
|
|
clock: $clock,
|
|
logger: null,
|
|
queue: $reportQueue,
|
|
asyncProcessing: false,
|
|
processors: [],
|
|
filters: []
|
|
);
|
|
|
|
// Create ErrorHandler
|
|
$errorHandler = new ErrorHandler(
|
|
emitter: $emitter,
|
|
container: $container,
|
|
requestIdGenerator: $requestIdGenerator,
|
|
errorAggregator: $errorAggregator,
|
|
errorReporter: $errorReporter,
|
|
logger: null,
|
|
isDebugMode: true,
|
|
securityHandler: null
|
|
);
|
|
|
|
echo "=== Testing Error Pipeline ===\n\n";
|
|
|
|
// Create test exception
|
|
$exception = FrameworkException::create(
|
|
DatabaseErrorCode::QUERY_FAILED,
|
|
'Test database error'
|
|
);
|
|
|
|
echo "1. Creating ErrorHandlerContext manually...\n";
|
|
$errorHandlerContext = (new \ReflectionClass($errorHandler))->getMethod('createErrorHandlerContext')->invoke($errorHandler, $exception, null);
|
|
echo " Context created successfully\n";
|
|
echo " ExceptionContext: operation={$errorHandlerContext->exception->operation}, component={$errorHandlerContext->exception->component}\n";
|
|
|
|
echo "\n2. Testing ErrorEvent::fromErrorHandlerContext...\n";
|
|
try {
|
|
$errorEvent = \App\Framework\ErrorAggregation\ErrorEvent::fromErrorHandlerContext($errorHandlerContext, $clock);
|
|
echo " ErrorEvent created successfully\n";
|
|
echo " Event ID: {$errorEvent->id}\n";
|
|
echo " Event message: {$errorEvent->errorMessage}\n";
|
|
} catch (\Throwable $e) {
|
|
echo " EXCEPTION in fromErrorHandlerContext: " . $e->getMessage() . "\n";
|
|
echo " AT: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
|
echo " TRACE:\n";
|
|
foreach ($e->getTrace() as $frame) {
|
|
$file = $frame['file'] ?? 'unknown';
|
|
$line = $frame['line'] ?? 0;
|
|
$function = $frame['function'] ?? 'unknown';
|
|
echo " $file:$line - $function()\n";
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
echo "\n3. Testing ErrorAggregator.processError directly...\n";
|
|
try {
|
|
$errorAggregator->processError($errorHandlerContext);
|
|
echo " processError completed successfully\n";
|
|
} catch (\Throwable $e) {
|
|
echo " EXCEPTION in processError: " . $e->getMessage() . "\n";
|
|
echo " AT: " . $e->getFile() . ":" . $e->getLine() . "\n";
|
|
throw $e;
|
|
}
|
|
|
|
echo "\n3. Creating HTTP response from exception...\n";
|
|
$response = $errorHandler->createHttpResponse($exception);
|
|
|
|
echo "\n4. Checking ErrorAggregator storage...\n";
|
|
$events = $errorStorage->getRecentEvents(10);
|
|
echo " Events stored: " . count($events) . "\n";
|
|
if (count($events) > 0) {
|
|
echo " First event message: " . $events[0]->message . "\n";
|
|
echo " First event severity: " . $events[0]->severity->value . "\n";
|
|
} else {
|
|
echo " No events stored!\n";
|
|
}
|
|
|
|
echo "\n3. Checking ErrorReporter storage...\n";
|
|
$reports = $errorReportStorage->findRecent(10);
|
|
echo " Reports stored: " . count($reports) . "\n";
|
|
if (count($reports) > 0) {
|
|
echo " First report message: " . $reports[0]->message . "\n";
|
|
echo " First report exception: " . $reports[0]->exception . "\n";
|
|
echo " First report level: " . $reports[0]->level . "\n";
|
|
} else {
|
|
echo " No reports stored!\n";
|
|
}
|
|
|
|
echo "\n=== Test Complete ===\n";
|