refactor(console, id, config): Dialog mode in Console, consolidated id modul, added config support for ini directives
This commit is contained in:
@@ -4,6 +4,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Logging\Formatter;
|
||||
|
||||
use App\Framework\Console\CliSapi;
|
||||
use App\Framework\Console\ConsoleStyle;
|
||||
use App\Framework\Console\TerminalDetector;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Logging\LogRecord;
|
||||
|
||||
@@ -17,6 +20,21 @@ final readonly class DevelopmentFormatter implements LogFormatter
|
||||
private bool $colorOutput = true
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob Farben verwendet werden sollen (berücksichtigt Terminal-Detection)
|
||||
*/
|
||||
private function shouldUseColors(): bool
|
||||
{
|
||||
// Wenn colorOutput explizit false, keine Farben
|
||||
if (!$this->colorOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prüfe Terminal-Detection mit CliSapi
|
||||
$cliSapi = CliSapi::detect();
|
||||
return $cliSapi->supportsColors($cliSapi->stdout);
|
||||
}
|
||||
|
||||
public function __invoke(LogRecord $record): string
|
||||
{
|
||||
@@ -26,7 +44,7 @@ final readonly class DevelopmentFormatter implements LogFormatter
|
||||
$message = $record->getMessage();
|
||||
|
||||
// Color coding for levels
|
||||
$levelString = $this->colorOutput ? $this->colorizeLevel($level) : $level->getName();
|
||||
$levelString = $this->shouldUseColors() ? $this->colorizeLevel($level) : $level->getName();
|
||||
|
||||
$output = sprintf(
|
||||
"%s [%s] %s.%s: %s\n",
|
||||
@@ -54,20 +72,11 @@ final readonly class DevelopmentFormatter implements LogFormatter
|
||||
|
||||
private function colorizeLevel(LogLevel $level): string
|
||||
{
|
||||
if (! $this->colorOutput) {
|
||||
return $level->getName();
|
||||
}
|
||||
|
||||
return match($level) {
|
||||
LogLevel::DEBUG => "\033[36m" . $level->getName() . "\033[0m", // Cyan
|
||||
LogLevel::INFO => "\033[32m" . $level->getName() . "\033[0m", // Green
|
||||
LogLevel::NOTICE => "\033[34m" . $level->getName() . "\033[0m", // Blue
|
||||
LogLevel::WARNING => "\033[33m" . $level->getName() . "\033[0m", // Yellow
|
||||
LogLevel::ERROR => "\033[31m" . $level->getName() . "\033[0m", // Red
|
||||
LogLevel::CRITICAL => "\033[35m" . $level->getName() . "\033[0m", // Magenta
|
||||
LogLevel::ALERT => "\033[41m" . $level->getName() . "\033[0m", // Red background
|
||||
LogLevel::EMERGENCY => "\033[41;37m" . $level->getName() . "\033[0m", // Red bg, white text
|
||||
};
|
||||
// Verwende LogLevel::getConsoleColor() und ConsoleStyle statt hardcoded ANSI-Codes
|
||||
$consoleColor = $level->getConsoleColor();
|
||||
$style = ConsoleStyle::create(color: $consoleColor);
|
||||
|
||||
return $style->apply($level->getName());
|
||||
}
|
||||
|
||||
private function formatContext(array $context): string
|
||||
|
||||
@@ -4,15 +4,34 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Logging\Formatter;
|
||||
|
||||
use App\Framework\Console\CliSapi;
|
||||
use App\Framework\Console\ConsoleStyle;
|
||||
use App\Framework\Console\TerminalDetector;
|
||||
use App\Framework\Logging\LogRecord;
|
||||
|
||||
final readonly class LineFormatter implements LogFormatter
|
||||
{
|
||||
public function __construct(
|
||||
private string $format = "[{timestamp}] {channel}.{level}: {message} {context}",
|
||||
private string $timestampFormat = 'Y-m-d H:i:s'
|
||||
private string $timestampFormat = 'Y-m-d H:i:s',
|
||||
private bool $colorOutput = false
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob Farben verwendet werden sollen (berücksichtigt Terminal-Detection)
|
||||
*/
|
||||
private function shouldUseColors(): bool
|
||||
{
|
||||
// Wenn colorOutput explizit false, keine Farben
|
||||
if (!$this->colorOutput) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prüfe Terminal-Detection mit CliSapi (für stderr, da Web-Requests auf stderr loggen)
|
||||
$cliSapi = CliSapi::detect();
|
||||
return $cliSapi->supportsColors($cliSapi->stderr);
|
||||
}
|
||||
|
||||
public function __invoke(LogRecord $record): string
|
||||
{
|
||||
@@ -33,11 +52,17 @@ final readonly class LineFormatter implements LogFormatter
|
||||
? "[{$record->getChannel()}] "
|
||||
: '';
|
||||
|
||||
// Level-String mit optionalen Farben
|
||||
$levelName = $record->level->getName();
|
||||
$coloredLevel = $this->shouldUseColors()
|
||||
? $this->colorizeLevel($record->level)
|
||||
: $levelName;
|
||||
|
||||
$replacements = [
|
||||
'{timestamp}' => $record->getFormattedTimestamp($this->timestampFormat),
|
||||
'{channel}' => $record->channel ?? 'app',
|
||||
'{level}' => $record->level->getName(),
|
||||
'{level_name}' => $record->level->getName(),
|
||||
'{level}' => $coloredLevel,
|
||||
'{level_name}' => $coloredLevel,
|
||||
'{message}' => $record->message,
|
||||
'{context}' => $contextString,
|
||||
'{request_id}' => $requestId,
|
||||
@@ -45,4 +70,16 @@ final readonly class LineFormatter implements LogFormatter
|
||||
|
||||
return strtr($this->format, $replacements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Färbt den Level-String basierend auf LogLevel
|
||||
*/
|
||||
private function colorizeLevel(\App\Framework\Logging\LogLevel $level): string
|
||||
{
|
||||
// Verwende LogLevel::getConsoleColor() und ConsoleStyle statt hardcoded ANSI-Codes
|
||||
$consoleColor = $level->getConsoleColor();
|
||||
$style = ConsoleStyle::create(color: $consoleColor);
|
||||
|
||||
return $style->apply($level->getName());
|
||||
}
|
||||
}
|
||||
|
||||
190
src/Framework/Logging/HandlerFactory.php
Normal file
190
src/Framework/Logging/HandlerFactory.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Logging;
|
||||
|
||||
use App\Framework\Config\EnvKey;
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Config\TypedConfiguration;
|
||||
use App\Framework\Console\CliSapi;
|
||||
use App\Framework\Console\TerminalDetector;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Logging\Formatter\DevelopmentFormatter;
|
||||
use App\Framework\Logging\Formatter\LineFormatter;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\Handlers\DockerJsonHandler;
|
||||
use App\Framework\Logging\Handlers\FileHandler;
|
||||
use App\Framework\Logging\Handlers\MultiFileHandler;
|
||||
use App\Framework\Queue\Queue;
|
||||
|
||||
/**
|
||||
* Factory für Log-Handler-Erstellung basierend auf Umgebung und Konfiguration.
|
||||
*/
|
||||
final readonly class HandlerFactory
|
||||
{
|
||||
/**
|
||||
* Erstellt alle Handler basierend auf der Umgebung
|
||||
*
|
||||
* @param TypedConfiguration $config
|
||||
* @param Environment $env
|
||||
* @param LogConfig $logConfig
|
||||
* @param PathProvider $pathProvider
|
||||
* @param LogLevel $minLevel
|
||||
* @param Queue $queue
|
||||
* @return array<LogHandler>
|
||||
*/
|
||||
public function createHandlers(
|
||||
TypedConfiguration $config,
|
||||
Environment $env,
|
||||
LogConfig $logConfig,
|
||||
PathProvider $pathProvider,
|
||||
LogLevel $minLevel,
|
||||
Queue $queue
|
||||
): array {
|
||||
$handlers = [];
|
||||
|
||||
// Console/Docker Logging Handler - für CLI und Web-Requests
|
||||
if (PHP_SAPI === 'cli') {
|
||||
// CLI: Docker JSON oder Console Handler
|
||||
$handlers[] = $this->createCliHandler($config, $env, $minLevel);
|
||||
} else {
|
||||
// Web-Requests: Console Handler auf stderr mit intelligenter Formatter-Auswahl
|
||||
$cliSapi = CliSapi::detect();
|
||||
$colorOutput = $this->determineColorOutput($env, $cliSapi->stderr);
|
||||
$handlers[] = $this->createWebHandler($minLevel, $colorOutput);
|
||||
}
|
||||
|
||||
// MultiFileHandler für automatisches Channel-Routing
|
||||
$multiFileFormatter = new LineFormatter();
|
||||
|
||||
$handlers[] = new MultiFileHandler(
|
||||
$logConfig,
|
||||
$pathProvider,
|
||||
$multiFileFormatter,
|
||||
$minLevel,
|
||||
0644
|
||||
);
|
||||
|
||||
// Fallback FileHandler für Kompatibilität (nur für 'app' Channel ohne Channel-Info)
|
||||
$fileFormatter = new LineFormatter(
|
||||
format: 'Line Formatter: [{timestamp}] [{level_name}] {request_id}{channel}{message}',
|
||||
timestampFormat: 'Y-m-d H:i:s'
|
||||
);
|
||||
$handlers[] = new FileHandler(
|
||||
$fileFormatter,
|
||||
$logConfig->getLogPath('app'),
|
||||
$minLevel,
|
||||
0644,
|
||||
null,
|
||||
$pathProvider
|
||||
);
|
||||
|
||||
return $handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt den CLI-Handler (Docker JSON oder Console)
|
||||
*/
|
||||
private function createCliHandler(
|
||||
TypedConfiguration $config,
|
||||
Environment $env,
|
||||
LogLevel $minLevel
|
||||
): LogHandler {
|
||||
// Prüfe ob wir in Docker laufen (für strukturierte JSON-Logs)
|
||||
$inDocker = file_exists('/.dockerenv') || getenv('DOCKER_CONTAINER') === 'true';
|
||||
|
||||
if ($inDocker) {
|
||||
if ($config->app->isProduction()) {
|
||||
// Production Docker: Compact JSON für Log-Aggregatoren mit Redaction
|
||||
return new DockerJsonHandler(
|
||||
env: $env,
|
||||
minLevel: $minLevel,
|
||||
redactSensitiveData: true // Auto-redact in Production
|
||||
);
|
||||
} else {
|
||||
// Development Docker: Pretty JSON für bessere Lesbarkeit
|
||||
return new DockerJsonHandler(
|
||||
env: $env,
|
||||
serviceName: $config->app->name ?? 'app',
|
||||
minLevel: $minLevel,
|
||||
prettyPrint: true, // Pretty-print für Development
|
||||
redactSensitiveData: false // Keine Redaction in Development für Debugging
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Lokale Entwicklung: Console Handler mit intelligenter Formatter-Auswahl
|
||||
$cliSapi = CliSapi::detect();
|
||||
$colorOutput = $this->determineColorOutput($env, $cliSapi->stdout);
|
||||
return $this->createCliConsoleHandler($minLevel, $colorOutput);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen Web-Request ConsoleHandler mit intelligenter Formatter-Auswahl
|
||||
*/
|
||||
private function createWebHandler(LogLevel $minLevel, bool $colorOutput): ConsoleHandler
|
||||
{
|
||||
$developmentFormatter = new DevelopmentFormatter(
|
||||
includeStackTrace: true,
|
||||
colorOutput: $colorOutput
|
||||
);
|
||||
|
||||
$lineFormatter = new LineFormatter(
|
||||
colorOutput: $colorOutput
|
||||
);
|
||||
|
||||
// Intelligente Formatter-Auswahl: Array mit beiden Formattern
|
||||
return new ConsoleHandler(
|
||||
[
|
||||
'development' => $developmentFormatter,
|
||||
'line' => $lineFormatter,
|
||||
],
|
||||
$minLevel,
|
||||
debugOnly: false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt einen CLI ConsoleHandler mit intelligenter Formatter-Auswahl
|
||||
*/
|
||||
private function createCliConsoleHandler(LogLevel $minLevel, bool $colorOutput): ConsoleHandler
|
||||
{
|
||||
$developmentFormatter = new DevelopmentFormatter(
|
||||
includeStackTrace: true,
|
||||
colorOutput: $colorOutput
|
||||
);
|
||||
|
||||
$lineFormatter = new LineFormatter(
|
||||
colorOutput: $colorOutput
|
||||
);
|
||||
|
||||
// Intelligente Formatter-Auswahl: Array mit beiden Formattern
|
||||
return new ConsoleHandler(
|
||||
[
|
||||
'development' => $developmentFormatter,
|
||||
'line' => $lineFormatter,
|
||||
],
|
||||
$minLevel
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bestimmt ob Farb-Output aktiviert werden soll
|
||||
*
|
||||
* @param Environment $env Environment für Konfiguration
|
||||
* @param \App\Framework\Console\ValueObjects\TerminalStream $stream Terminal Stream für Terminal-Detection
|
||||
* @return bool True wenn Farben aktiviert werden sollen
|
||||
*/
|
||||
private function determineColorOutput(Environment $env, \App\Framework\Console\ValueObjects\TerminalStream $stream): bool
|
||||
{
|
||||
$colorConfig = $env->get(EnvKey::LOG_COLOR_OUTPUT, 'auto');
|
||||
|
||||
return match (strtolower((string) $colorConfig)) {
|
||||
'true', '1', 'yes', 'on' => true,
|
||||
'false', '0', 'no', 'off' => false,
|
||||
default => TerminalDetector::supportsColors($stream), // 'auto' oder default
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace App\Framework\Logging\Handlers;
|
||||
|
||||
use App\Framework\Logging\LogHandler;
|
||||
use App\Framework\Logging\Formatter\DevelopmentFormatter;
|
||||
use App\Framework\Logging\Formatter\LineFormatter;
|
||||
use App\Framework\Logging\Formatter\LogFormatter;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Logging\LogRecord;
|
||||
@@ -12,6 +14,10 @@ use App\Framework\Logging\LogRecord;
|
||||
/**
|
||||
* Handler für die Ausgabe von Log-Einträgen in der Konsole.
|
||||
*
|
||||
* Unterstützt intelligente Formatter-Auswahl basierend auf LogLevel und Context:
|
||||
* - ERROR/CRITICAL/ALERT/EMERGENCY oder Exception → DevelopmentFormatter (detailliert)
|
||||
* - DEBUG/INFO/NOTICE/WARNING → LineFormatter (kompakt)
|
||||
*
|
||||
* Bei CLI: Nutzt stderr für WARNING+ und stdout für niedrigere Levels.
|
||||
* Bei Web-Requests: Alle Logs gehen auf stderr (POSIX-konform, Docker-kompatibel).
|
||||
*/
|
||||
@@ -28,26 +34,53 @@ class ConsoleHandler implements LogHandler
|
||||
private bool $debugOnly;
|
||||
|
||||
/**
|
||||
* @var LogFormatter Formatter für die Log-Ausgabe
|
||||
* @var LogFormatter|null Formatter für die Log-Ausgabe (bei einfachem Modus)
|
||||
*/
|
||||
private LogFormatter $formatter;
|
||||
private ?LogFormatter $formatter = null;
|
||||
|
||||
/**
|
||||
* @var DevelopmentFormatter|null Formatter für detaillierte Ausgabe (bei intelligentem Modus)
|
||||
*/
|
||||
private ?DevelopmentFormatter $developmentFormatter = null;
|
||||
|
||||
/**
|
||||
* @var LineFormatter|null Formatter für kompakte Ausgabe (bei intelligentem Modus)
|
||||
*/
|
||||
private ?LineFormatter $lineFormatter = null;
|
||||
|
||||
/**
|
||||
* @var bool Ob intelligente Formatter-Auswahl aktiviert ist
|
||||
*/
|
||||
private bool $intelligentSelection;
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen ConsoleHandler
|
||||
*
|
||||
* @param LogFormatter $formatter Formatter für die Log-Ausgabe
|
||||
* @param LogFormatter|array{development: DevelopmentFormatter, line: LineFormatter}|null $formatter
|
||||
* Einzelner Formatter (rückwärtskompatibel) oder Array mit 'development' und 'line' Formattern für intelligente Auswahl
|
||||
* @param LogLevel|int $minLevel Minimales Level, ab dem dieser Handler aktiv wird
|
||||
* @param bool $debugOnly Ob der Handler nur im Debug-Modus aktiv ist
|
||||
* @param LogLevel $stderrLevel Level ab dem stderr verwendet wird (default: WARNING)
|
||||
*/
|
||||
public function __construct(
|
||||
LogFormatter $formatter,
|
||||
LogFormatter|array|null $formatter = null,
|
||||
LogLevel|int $minLevel = LogLevel::DEBUG,
|
||||
bool $debugOnly = true,
|
||||
private readonly LogLevel $stderrLevel = LogLevel::WARNING,
|
||||
) {
|
||||
$this->formatter = $formatter;
|
||||
$this->minLevel = $minLevel instanceof LogLevel ? $minLevel : LogLevel::fromValue($minLevel);
|
||||
$this->debugOnly = $debugOnly;
|
||||
|
||||
// Intelligente Formatter-Auswahl wenn Array übergeben wird
|
||||
if (is_array($formatter)) {
|
||||
$this->intelligentSelection = true;
|
||||
$this->developmentFormatter = $formatter['development'] ?? null;
|
||||
$this->lineFormatter = $formatter['line'] ?? null;
|
||||
} else {
|
||||
// Rückwärtskompatibel: Einzelner Formatter
|
||||
$this->intelligentSelection = false;
|
||||
$this->formatter = $formatter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,8 +101,11 @@ class ConsoleHandler implements LogHandler
|
||||
*/
|
||||
public function handle(LogRecord $record): void
|
||||
{
|
||||
// Formatter auswählen (intelligent oder einfach)
|
||||
$formatter = $this->selectFormatter($record);
|
||||
|
||||
// Formatter verwenden für Formatierung
|
||||
$formatted = ($this->formatter)($record);
|
||||
$formatted = $formatter($record);
|
||||
|
||||
// Formatter gibt immer string zurück für Console
|
||||
$output = is_string($formatted)
|
||||
@@ -96,9 +132,57 @@ class ConsoleHandler implements LogHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Formatter zurück
|
||||
* Wählt den passenden Formatter basierend auf LogLevel und Context
|
||||
*/
|
||||
public function getFormatter(): LogFormatter
|
||||
private function selectFormatter(LogRecord $record): LogFormatter
|
||||
{
|
||||
// Wenn kein intelligenter Modus, verwende den einzelnen Formatter
|
||||
if (!$this->intelligentSelection) {
|
||||
if ($this->formatter === null) {
|
||||
throw new \RuntimeException('ConsoleHandler: No formatter configured');
|
||||
}
|
||||
return $this->formatter;
|
||||
}
|
||||
|
||||
// Prüfe ob Exception vorhanden ist
|
||||
if ($this->hasException($record)) {
|
||||
return $this->developmentFormatter ?? $this->lineFormatter ?? throw new \RuntimeException('ConsoleHandler: No formatter configured');
|
||||
}
|
||||
|
||||
// Für ERROR, CRITICAL, ALERT, EMERGENCY: DevelopmentFormatter (detailliert)
|
||||
if ($record->level->value >= LogLevel::ERROR->value) {
|
||||
return $this->developmentFormatter ?? $this->lineFormatter ?? throw new \RuntimeException('ConsoleHandler: No formatter configured');
|
||||
}
|
||||
|
||||
// Für DEBUG, INFO, NOTICE, WARNING: LineFormatter (kompakt)
|
||||
return $this->lineFormatter ?? $this->developmentFormatter ?? throw new \RuntimeException('ConsoleHandler: No formatter configured');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft ob der LogRecord eine Exception enthält
|
||||
*/
|
||||
private function hasException(LogRecord $record): bool
|
||||
{
|
||||
// Prüfe in extras
|
||||
if ($record->hasExtra('exception_class') || $record->hasExtra('exception')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Prüfe in context
|
||||
$context = $record->getContext();
|
||||
if (isset($context['exception']) || isset($context['exception_class'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Formatter zurück (rückwärtskompatibel)
|
||||
*
|
||||
* @deprecated Verwende selectFormatter() für intelligente Auswahl
|
||||
*/
|
||||
public function getFormatter(): ?LogFormatter
|
||||
{
|
||||
return $this->formatter;
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Logging;
|
||||
|
||||
use App\Framework\DateTime\Clock;
|
||||
use App\Framework\DateTime\SystemClock;
|
||||
|
||||
/**
|
||||
* Factory für Logger-Instanzen.
|
||||
*
|
||||
* @deprecated Verwende stattdessen Dependency Injection. Der Logger wird automatisch über LoggerInitializer erstellt.
|
||||
* Diese Klasse wird nur noch für Legacy-Code verwendet und sollte nicht in neuem Code genutzt werden.
|
||||
*/
|
||||
final class LoggerFactory
|
||||
{
|
||||
private static ?DefaultLogger $defaultLogger = null;
|
||||
|
||||
/**
|
||||
* Erzeugt einen neuen Logger mit optionalen Einstellungen.
|
||||
*
|
||||
* @deprecated Verwende stattdessen Dependency Injection. Der Logger wird automatisch über LoggerInitializer erstellt.
|
||||
*/
|
||||
public static function create(
|
||||
?Clock $clock = null,
|
||||
LogLevel|int $minLevel = LogLevel::DEBUG,
|
||||
array $handlers = []
|
||||
): DefaultLogger {
|
||||
$clock ??= new SystemClock();
|
||||
|
||||
return new DefaultLogger($clock, $minLevel, $handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Standard-Logger zurück oder erstellt ihn, falls er noch nicht existiert.
|
||||
*
|
||||
* @deprecated Verwende stattdessen Dependency Injection. Der Logger wird automatisch über LoggerInitializer erstellt.
|
||||
*/
|
||||
public static function getDefaultLogger(): DefaultLogger
|
||||
{
|
||||
if (self::$defaultLogger === null) {
|
||||
$debug = filter_var(getenv('APP_DEBUG'), FILTER_VALIDATE_BOOLEAN);
|
||||
$minLevel = $debug ? LogLevel::DEBUG : LogLevel::INFO;
|
||||
|
||||
self::$defaultLogger = self::create(null, $minLevel);
|
||||
}
|
||||
|
||||
return self::$defaultLogger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt einen benutzerdefinierten Logger als Standard-Logger.
|
||||
*
|
||||
* @deprecated Verwende stattdessen Dependency Injection.
|
||||
*/
|
||||
public static function setDefaultLogger(DefaultLogger $logger): void
|
||||
{
|
||||
self::$defaultLogger = $logger;
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,7 @@ use App\Framework\Core\PathProvider;
|
||||
use App\Framework\DateTime\Clock;
|
||||
use App\Framework\DI\Container;
|
||||
use App\Framework\DI\Initializer;
|
||||
use App\Framework\Logging\Formatter\DevelopmentFormatter;
|
||||
use App\Framework\Logging\Formatter\LineFormatter;
|
||||
use App\Framework\Logging\Handlers\ConsoleHandler;
|
||||
use App\Framework\Logging\Handlers\DockerJsonHandler;
|
||||
use App\Framework\Logging\Handlers\FileHandler;
|
||||
use App\Framework\Logging\Handlers\MultiFileHandler;
|
||||
use App\Framework\Logging\Handlers\NullHandler;
|
||||
use App\Framework\Queue\Queue;
|
||||
use App\Framework\Queue\FileQueue;
|
||||
use App\Framework\Redis\RedisConfig;
|
||||
use App\Framework\Redis\RedisConnection;
|
||||
|
||||
final readonly class LoggerInitializer
|
||||
{
|
||||
@@ -44,8 +34,13 @@ final readonly class LoggerInitializer
|
||||
$processorManager = new ProcessorManager();
|
||||
$minLevel = $this->determineMinLogLevel($config);
|
||||
$logConfig = $this->initializeLogConfig($pathProvider);
|
||||
$queue = $this->createQueue($pathProvider);
|
||||
$handlers = $this->createHandlers($config, $env, $logConfig, $pathProvider, $minLevel, $queue);
|
||||
|
||||
$queueFactory = new QueueFactory();
|
||||
$queue = $queueFactory->createQueue($pathProvider, $env);
|
||||
|
||||
$handlerFactory = new HandlerFactory();
|
||||
$handlers = $handlerFactory->createHandlers($config, $env, $logConfig, $pathProvider, $minLevel, $queue);
|
||||
|
||||
$contextManager = $container->get(LogContextManager::class);
|
||||
$clock = $container->get(Clock::class);
|
||||
|
||||
@@ -113,117 +108,4 @@ final readonly class LoggerInitializer
|
||||
return $logConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt die Queue für asynchrones Logging
|
||||
*/
|
||||
private function createQueue(PathProvider $pathProvider): Queue
|
||||
{
|
||||
#$redisConfig = RedisConfig::fromEnvironment($env);
|
||||
#$redisConnection = new RedisConnection($redisConfig, 'queue');
|
||||
|
||||
#return new RedisQueue($redisConnection, 'commands');
|
||||
|
||||
$queuePath = $pathProvider->resolvePath('storage/queue');
|
||||
return new FileQueue($queuePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt alle Handler basierend auf der Umgebung
|
||||
*
|
||||
* @param TypedConfiguration $config
|
||||
* @param Environment $env
|
||||
* @param LogConfig $logConfig
|
||||
* @param PathProvider $pathProvider
|
||||
* @param LogLevel $minLevel
|
||||
* @param Queue $queue
|
||||
* @return array<LogHandler>
|
||||
*/
|
||||
private function createHandlers(
|
||||
TypedConfiguration $config,
|
||||
Environment $env,
|
||||
LogConfig $logConfig,
|
||||
PathProvider $pathProvider,
|
||||
LogLevel $minLevel,
|
||||
Queue $queue
|
||||
): array {
|
||||
$handlers = [];
|
||||
|
||||
// Console/Docker Logging Handler - für CLI und Web-Requests
|
||||
if (PHP_SAPI === 'cli') {
|
||||
// CLI: Docker JSON oder Console Handler
|
||||
$handlers[] = $this->createCliHandler($config, $env, $minLevel);
|
||||
} else {
|
||||
// Web-Requests: Console Handler auf stderr
|
||||
$webFormatter = new LineFormatter();
|
||||
$handlers[] = new ConsoleHandler($webFormatter, $minLevel, debugOnly: false);
|
||||
}
|
||||
|
||||
//$handlers[] = new QueuedLogHandler($queue);
|
||||
|
||||
// MultiFileHandler für automatisches Channel-Routing
|
||||
$multiFileFormatter = new LineFormatter();
|
||||
|
||||
$handlers[] = new MultiFileHandler(
|
||||
$logConfig,
|
||||
$pathProvider,
|
||||
$multiFileFormatter,
|
||||
$minLevel,
|
||||
0644
|
||||
);
|
||||
|
||||
// Fallback FileHandler für Kompatibilität (nur für 'app' Channel ohne Channel-Info)
|
||||
$fileFormatter = new LineFormatter(
|
||||
format: 'Line Formatter: [{timestamp}] [{level_name}] {request_id}{channel}{message}',
|
||||
timestampFormat: 'Y-m-d H:i:s'
|
||||
);
|
||||
$handlers[] = new FileHandler(
|
||||
$fileFormatter,
|
||||
$logConfig->getLogPath('app'),
|
||||
$minLevel,
|
||||
0644,
|
||||
null,
|
||||
$pathProvider
|
||||
);
|
||||
|
||||
return $handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt den CLI-Handler (Docker JSON oder Console)
|
||||
*/
|
||||
private function createCliHandler(
|
||||
TypedConfiguration $config,
|
||||
Environment $env,
|
||||
LogLevel $minLevel
|
||||
): LogHandler {
|
||||
// Prüfe ob wir in Docker laufen (für strukturierte JSON-Logs)
|
||||
$inDocker = file_exists('/.dockerenv') || getenv('DOCKER_CONTAINER') === 'true';
|
||||
|
||||
if ($inDocker) {
|
||||
if ($config->app->isProduction()) {
|
||||
// Production Docker: Compact JSON für Log-Aggregatoren mit Redaction
|
||||
return new DockerJsonHandler(
|
||||
env: $env,
|
||||
minLevel: $minLevel,
|
||||
redactSensitiveData: true // Auto-redact in Production
|
||||
);
|
||||
} else {
|
||||
// Development Docker: Pretty JSON für bessere Lesbarkeit
|
||||
return new DockerJsonHandler(
|
||||
env: $env,
|
||||
serviceName: $config->app->name ?? 'app',
|
||||
minLevel: $minLevel,
|
||||
prettyPrint: true, // Pretty-print für Development
|
||||
redactSensitiveData: false // Keine Redaction in Development für Debugging
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Lokale Entwicklung: Farbige Console-Logs
|
||||
$consoleFormatter = new DevelopmentFormatter(
|
||||
includeStackTrace: true,
|
||||
colorOutput: true
|
||||
);
|
||||
return new ConsoleHandler($consoleFormatter, $minLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
39
src/Framework/Logging/QueueFactory.php
Normal file
39
src/Framework/Logging/QueueFactory.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\Logging;
|
||||
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Queue\FileQueue;
|
||||
use App\Framework\Queue\Queue;
|
||||
|
||||
/**
|
||||
* Factory für Queue-Instanzen für asynchrones Logging.
|
||||
*
|
||||
* Unterstützt aktuell FileQueue, kann in Zukunft um RedisQueue erweitert werden.
|
||||
*/
|
||||
final readonly class QueueFactory
|
||||
{
|
||||
/**
|
||||
* Erstellt eine Queue-Instanz basierend auf der Konfiguration
|
||||
*
|
||||
* @param PathProvider $pathProvider Path provider für File-basierte Queues
|
||||
* @param Environment|null $env Optional: Environment für zukünftige Redis-Integration
|
||||
* @return Queue
|
||||
*/
|
||||
public function createQueue(PathProvider $pathProvider, ?Environment $env = null): Queue
|
||||
{
|
||||
// TODO: Redis-Queue Integration wenn benötigt
|
||||
// if ($env && $this->shouldUseRedisQueue($env)) {
|
||||
// $redisConfig = RedisConfig::fromEnvironment($env);
|
||||
// $redisConnection = new RedisConnection($redisConfig, 'queue');
|
||||
// return new RedisQueue($redisConnection, 'commands');
|
||||
// }
|
||||
|
||||
$queuePath = $pathProvider->resolvePath('storage/queue');
|
||||
return new FileQueue($queuePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Framework\Logging\ValueObjects;
|
||||
|
||||
use App\Framework\Http\ServerRequest;
|
||||
use App\Framework\Ulid\Ulid;
|
||||
use App\Framework\Id\Ulid\Ulid;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user