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

@@ -1,22 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Framework\Queue;
use App\Framework\Cache\Serializer;
use App\Framework\Cache\Serializer\PhpSerializer;
use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\Logger;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\ProcessorManager;
use App\Framework\Serializer\Php\PhpSerializer;
use App\Framework\Serializer\Serializer;
final readonly class FileQueue implements Queue
{
private string $queueDir;
private Logger $logger;
public function __construct(
string $queueDir,
private Serializer $serializer = new PhpSerializer())
{
private Serializer $serializer = new PhpSerializer(),
?Logger $logger = null
) {
$this->queueDir = $queueDir;
if (!is_dir($queueDir)) {
if (! is_dir($queueDir)) {
mkdir($queueDir, 0777, true);
}
// Use provided logger or create a null logger for production
$this->logger = $logger ?? new DefaultLogger(
minLevel: LogLevel::WARNING,
handlers: [],
processorManager: new ProcessorManager()
);
}
public function push(object $job): void
@@ -25,46 +41,32 @@ final readonly class FileQueue implements Queue
$jobHash = md5($this->serializer->serialize($job));
$hashFile = $this->queueDir . '/hash_' . $jobHash . '.job';
// DETAILED DEBUG: Wer ruft push() auf?
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
$caller = '';
foreach ($trace as $frame) {
if (isset($frame['file']) && isset($frame['line'])) {
$caller .= basename($frame['file']) . ':' . $frame['line'] . ' -> ';
}
}
error_log("FileQueue Debug: PUSH called from: " . $caller);
error_log("FileQueue Debug: Job class: " . get_class($job));
error_log("FileQueue Debug: Job data: " . json_encode($job));
error_log("FileQueue Debug: Job hash: " . $jobHash);
// Debug logging removed for production
// Prüfe ob identischer Job bereits existiert
if (file_exists($hashFile)) {
error_log("FileQueue Debug: ⚠️ DUPLICATE JOB REJECTED - hash already exists: " . $jobHash);
error_log("FileQueue Debug: Called from: " . $caller);
// Silent deduplication - no logging needed in production
return; // Job wird nicht hinzugefügt
}
// Verwende Hash als Dateiname für automatische Deduplication
file_put_contents($hashFile, $this->serializer->serialize($job));
error_log("FileQueue Debug: ✅ Job added with hash: " . $jobHash);
// No logging for successful job addition in production
}
public function pop(): ?object
{
$files = glob($this->queueDir . '/*.job');
// Debug: Queue-Status loggen
// Queue-Status - no logging in production
if (empty($files)) {
return null;
}
error_log("FileQueue Debug: Found " . count($files) . " job files in " . $this->queueDir);
error_log("FileQueue Debug: Files: " . implode(', ', array_map('basename', $files)));
// No debug logging for job discovery in production
// Sortiere nach Erstellungsdatum (FIFO)
usort($files, function($a, $b) {
usort($files, function ($a, $b) {
return filemtime($a) <=> filemtime($b);
});
@@ -73,29 +75,32 @@ final readonly class FileQueue implements Queue
// Atomic Lock-Mechanismus um race conditions zu verhindern
if (file_exists($lockFile)) {
error_log("FileQueue Debug: Job file is locked: $file");
// Silent handling of locked files - normal operation
return null; // Job wird bereits verarbeitet
}
// Lock erstellen
if (!@touch($lockFile)) {
error_log("FileQueue Debug: Could not create lock file: $lockFile");
if (! @touch($lockFile)) {
$this->logger->warning("FileQueue: ⚠️ Could not create lock file", ['lock_file' => basename($lockFile)]);
return null;
}
try {
// Prüfe ob Datei noch existiert (race condition)
if (!file_exists($file) || !is_readable($file)) {
error_log("FileQueue Debug: Job file not accessible: $file");
if (! file_exists($file) || ! is_readable($file)) {
$this->logger->warning("FileQueue: ⚠️ Job file not accessible", ['file' => basename($file)]);
@unlink($lockFile);
return $this->pop();
}
$content = file_get_contents($file);
if ($content === false) {
error_log("FileQueue Debug: Could not read job file: $file");
$this->logger->error("FileQueue: ❌ Could not read job file", ['file' => basename($file)]);
@unlink($file);
@unlink($lockFile);
return $this->pop();
}
@@ -105,24 +110,29 @@ final readonly class FileQueue implements Queue
$tempFile = $file . '.deleting.' . time() . '.' . getmypid();
if (rename($file, $tempFile)) {
unlink($tempFile);
error_log("FileQueue Debug: Successfully deleted job file: " . basename($file));
// No logging for successful job completion in production
} else {
error_log("FileQueue Debug: Failed to rename job file for deletion: $file");
$this->logger->warning("FileQueue: ⚠️ Failed to rename job file for deletion", ['file' => basename($file)]);
// Fallback: direktes löschen versuchen
if (!@unlink($file)) {
error_log("FileQueue Debug: Failed to delete job file: $file - moving to processed");
if (! @unlink($file)) {
$this->logger->warning("FileQueue: ⚠️ Failed to delete job file - moving to processed", ['file' => basename($file)]);
$processedFile = $file . '.processed.' . time();
@rename($file, $processedFile);
}
}
@unlink($lockFile);
return $job;
} catch (\Throwable $e) {
error_log("FileQueue Debug: Failed to process job from file $file: " . $e->getMessage());
$this->logger->error("FileQueue: ❌ Failed to process job", [
'file' => basename($file),
'error' => $e->getMessage(),
]);
@unlink($file);
@unlink($lockFile);
return $this->pop();
}
}

View File

@@ -1,10 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Framework\Queue;
interface Queue
{
public function push(object $job): void;
public function pop(): ?object;
}

View File

@@ -1,29 +1,42 @@
<?php
declare(strict_types=1);
namespace App\Framework\Queue;
use App\Framework\Core\PathProvider;
use App\Framework\DI\Initializer;
use App\Framework\Logging\Logger;
use App\Framework\Redis\RedisConfig;
use App\Framework\Redis\RedisConnection;
final readonly class QueueInitializer
{
public function __construct(
private PathProvider $pathProvider
)
{
) {
}
#[Initializer]
public function __invoke(): Queue
public function __invoke(Logger $logger): Queue
{
$queue = new RedisQueue(
queueName: 'commands',
host: 'redis',
port: 6379,
try {
$redisConfig = new RedisConfig(
host: 'redis',
port: 6379,
database: 2 // Use DB 2 for queue
);
$redisConnection = new RedisConnection($redisConfig, 'queue');
);
return new RedisQueue($redisConnection, 'commands');
} catch (\Throwable $e) {
// Fallback to file queue if Redis is not available
$logger->warning("⚠️ Redis queue not available, falling back to file queue", [
'error' => $e->getMessage(),
]);
$path = $this->pathProvider->resolvePath('/src/Framework/CommandBus/storage/queue/');
$path = $this->pathProvider->resolvePath('/src/Framework/CommandBus/storage/queue/');
return new FileQueue($path);
return new FileQueue($path, logger: $logger);
}
}
}

View File

@@ -1,53 +1,41 @@
<?php
declare(strict_types=1);
namespace App\Framework\Queue;
use App\Framework\Cache\Serializer;
use App\Framework\Cache\Serializer\PhpSerializer;
use Predis\Client as Redis;
use App\Framework\Redis\RedisConnectionInterface;
use App\Framework\Serializer\Php\PhpSerializer;
use App\Framework\Serializer\Serializer;
use Redis;
final class RedisQueue implements Queue
{
private Redis $redis;
private string $queueName;
public function __construct(
string $queueName = 'commands',
string $host = '127.0.0.1',
int $port = 6379,
?string $password = null,
int $db = 0,
private readonly Serializer $serializer = new PhpSerializer
private RedisConnectionInterface $connection,
private string $queueName = 'commands',
private readonly Serializer $serializer = new PhpSerializer()
) {
$this->queueName = $queueName;
$this->redis = new Redis([
'scheme' => 'tcp',
'host' => $host,
'port' => $port,
'timeout' => 1.0,
'read_write_timeout' => 0,
]);
$this->redis->connect();
if ($password) {
$this->redis->auth($password);
}
$this->redis->select($db);
$this->redis = $this->connection->getClient();
}
public function push(object $job): void
{
$data = $this->serializer->serialize($job);
$this->redis->rpush($this->queueName, [$data]);
$this->redis->rPush($this->queueName, $data);
}
public function pop(): ?object
{
// Wartet bis zu 2 Sekunden auf einen neuen Job (blocking)
$result = $this->redis->blpop($this->queueName, 2);
$result = $this->redis->blPop([$this->queueName], 2);
if ($result && isset($result[1])) {
return $this->serializer->unserialize($result[1]);
}
return null;
}
}