Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,332 @@
<?php
declare(strict_types=1);
namespace App\Framework\Discovery\Factory;
use App\Framework\Cache\Cache;
use App\Framework\CommandBus\CommandHandlerMapper;
use App\Framework\Console\ConsoleCommandMapper;
use App\Framework\Core\AttributeMapper;
use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Core\Events\EventHandlerMapper;
use App\Framework\Core\PathProvider;
use App\Framework\Core\RouteMapper;
use App\Framework\Database\Migration\Migration;
use App\Framework\DateTime\Clock;
use App\Framework\DI\Container;
use App\Framework\DI\DefaultContainer;
use App\Framework\DI\InitializerMapper;
use App\Framework\Discovery\UnifiedDiscoveryService;
use App\Framework\Discovery\ValueObjects\DiscoveryConfiguration;
use App\Framework\Filesystem\FileSystemService;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Logging\Logger;
use App\Framework\Mcp\McpResourceMapper;
use App\Framework\Mcp\McpToolMapper;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\QueryBus\QueryHandlerMapper;
use App\Framework\Reflection\CachedReflectionProvider;
use App\Framework\Reflection\ReflectionProvider;
use App\Framework\Template\Processing\DomProcessor;
use App\Framework\Template\Processing\StringProcessor;
/**
* Factory for creating properly configured DiscoveryService instances
*
* Centralizes the complex dependency creation and configuration logic
* that was previously scattered in constructors and bootstrappers.
*/
final readonly class DiscoveryServiceFactory
{
public function __construct(
private Container $container,
private PathProvider $pathProvider,
private Cache $cache,
private Clock $clock
) {
}
/**
* Create a fully configured DiscoveryService
*/
public function create(DiscoveryConfiguration $config): UnifiedDiscoveryService
{
// Validate configuration
$config->validate();
// Resolve or create required dependencies
$reflectionProvider = $this->resolveReflectionProvider();
$fileSystemService = $this->resolveFileSystemService();
// Resolve optional dependencies based on configuration
$logger = $config->enablePerformanceTracking
? $this->resolveLogger()
: null;
$eventDispatcher = $config->enableEventDispatcher
? $this->resolveEventDispatcher()
: null;
$memoryMonitor = $config->enableMemoryMonitoring
? $this->resolveMemoryMonitor()
: null;
// Merge configured mappers with defaults
$attributeMappers = $this->buildAttributeMappers($config->attributeMappers);
$targetInterfaces = $this->buildTargetInterfaces($config->targetInterfaces);
return new UnifiedDiscoveryService(
pathProvider: $this->pathProvider,
cache: $this->cache,
clock: $this->clock,
reflectionProvider: $reflectionProvider,
configuration: $config,
attributeMappers: $attributeMappers,
targetInterfaces: $targetInterfaces,
logger: $logger,
eventDispatcher: $eventDispatcher,
memoryMonitor: $memoryMonitor,
fileSystemService: $fileSystemService
);
}
/**
* Create DiscoveryService for development environment
*/
public function createForDevelopment(array $paths = []): UnifiedDiscoveryService
{
$config = DiscoveryConfiguration::development();
$scanPaths = ! empty($paths) ? $paths : $this->getDefaultScanPaths();
$config = $config->withPaths($scanPaths);
return $this->create($config);
}
/**
* Create DiscoveryService for production environment
*/
public function createForProduction(array $paths = []): UnifiedDiscoveryService
{
$config = DiscoveryConfiguration::production();
$scanPaths = ! empty($paths) ? $paths : $this->getDefaultScanPaths();
$config = $config->withPaths($scanPaths);
return $this->create($config);
}
/**
* Create DiscoveryService for testing environment
*/
public function createForTesting(array $paths = []): UnifiedDiscoveryService
{
$config = DiscoveryConfiguration::testing();
$scanPaths = ! empty($paths) ? $paths : $this->getDefaultScanPaths();
$config = $config->withPaths($scanPaths);
return $this->create($config);
}
/**
* Create DiscoveryService with custom configuration
*/
public function createWithCustomConfig(
array $paths = [],
bool $useCache = true,
array $attributeMappers = [],
array $targetInterfaces = []
): UnifiedDiscoveryService {
$config = new DiscoveryConfiguration(
paths: $paths,
attributeMappers: $attributeMappers,
targetInterfaces: $targetInterfaces,
useCache: $useCache
);
return $this->create($config);
}
/**
* Resolve ReflectionProvider from container or create default
*/
private function resolveReflectionProvider(): ReflectionProvider
{
if ($this->container->has(ReflectionProvider::class)) {
return $this->container->get(ReflectionProvider::class);
}
return new CachedReflectionProvider();
}
/**
* Resolve FileSystemService from container or create default
*/
private function resolveFileSystemService(): FileSystemService
{
if ($this->container->has(FileSystemService::class)) {
return $this->container->get(FileSystemService::class);
}
return new FileSystemService();
}
/**
* Resolve Logger from container if available
*/
private function resolveLogger(): ?Logger
{
return $this->container->has(Logger::class)
? $this->container->get(Logger::class)
: null;
}
/**
* Resolve EventDispatcher from container if available
*/
private function resolveEventDispatcher(): ?EventDispatcher
{
return $this->container->has(EventDispatcher::class)
? $this->container->get(EventDispatcher::class)
: null;
}
/**
* Resolve MemoryMonitor from container if available
*/
private function resolveMemoryMonitor(): ?MemoryMonitor
{
return $this->container->has(MemoryMonitor::class)
? $this->container->get(MemoryMonitor::class)
: null;
}
/**
* Build attribute mappers array with defaults and user-provided mappers
*/
private function buildAttributeMappers(array $customMappers = []): array
{
$defaultMappers = [
new RouteMapper(),
new EventHandlerMapper(),
new \App\Framework\EventBus\EventHandlerMapper(),
new QueryHandlerMapper(),
new CommandHandlerMapper(),
new InitializerMapper(),
new McpToolMapper(),
new McpResourceMapper(),
new ConsoleCommandMapper(),
];
// Merge custom mappers with defaults, allowing override by class name
$mappers = [];
$mapperClasses = [];
// Add default mappers first
foreach ($defaultMappers as $mapper) {
$className = get_class($mapper);
$mappers[] = $mapper;
$mapperClasses[] = $className;
}
// Add custom mappers, replacing defaults if same class
foreach ($customMappers as $mapper) {
$className = get_class($mapper);
$existingIndex = array_search($className, $mapperClasses, true);
if ($existingIndex !== false) {
// Replace existing mapper
$mappers[$existingIndex] = $mapper;
} else {
// Add new mapper
$mappers[] = $mapper;
$mapperClasses[] = $className;
}
}
return $mappers;
}
/**
* Build target interfaces array with defaults and user-provided interfaces
*/
private function buildTargetInterfaces(array $customInterfaces = []): array
{
$defaultInterfaces = [
AttributeMapper::class,
HttpMiddleware::class,
DomProcessor::class,
StringProcessor::class,
Migration::class,
];
// Merge and deduplicate
return array_values(array_unique(array_merge($defaultInterfaces, $customInterfaces)));
}
/**
* Create a lightweight factory for specific use cases
*/
public static function lightweight(
PathProvider $pathProvider,
Cache $cache,
Clock $clock
): self {
// Create a minimal container for basic dependencies
$container = new DefaultContainer();
return new self($container, $pathProvider, $cache, $clock);
}
/**
* Validate that all required services are available
* @return string[]
*/
public function validateDependencies(DiscoveryConfiguration $config): array
{
$issues = [];
// Check required dependencies
if (! $this->container->has(PathProvider::class) && ! isset($this->pathProvider)) {
$issues[] = 'PathProvider is required but not available';
}
if (! $this->container->has(Cache::class) && ! isset($this->cache)) {
$issues[] = 'Cache is required but not available';
}
if (! $this->container->has(Clock::class) && ! isset($this->clock)) {
$issues[] = 'Clock is required but not available';
}
// Check optional dependencies if enabled
if ($config->enableEventDispatcher && ! $this->container->has(EventDispatcher::class)) {
$issues[] = 'EventDispatcher is enabled but not available in container';
}
if ($config->enableMemoryMonitoring && ! $this->container->has(MemoryMonitor::class)) {
$issues[] = 'MemoryMonitor is enabled but not available in container';
}
if ($config->enablePerformanceTracking && ! $this->container->has(Logger::class)) {
$issues[] = 'Logger is needed for performance tracking but not available in container';
}
return $issues;
}
/**
* Get default scan paths for discovery
* @return string[]
*/
private function getDefaultScanPaths(): array
{
$basePath = $this->pathProvider->getBasePath();
return [
$this->pathProvider->getSourcePath(),
#$basePath . '/src',
#$basePath . '/app', // Support for app/ directory structure
];
}
}