198 lines
6.4 KiB
PHP
198 lines
6.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\Logging;
|
|
|
|
use App\Framework\Attributes\Singleton;
|
|
use App\Framework\DateTime\Clock;
|
|
use App\Framework\Logging\ValueObjects\LogContext;
|
|
use DateMalformedStringException;
|
|
|
|
/**
|
|
* Einfacher Logger für das Framework.
|
|
*/
|
|
#[Singleton]
|
|
final readonly class DefaultLogger implements Logger, SupportsChannels
|
|
{
|
|
private ChannelLoggerRegistry $channelRegistry;
|
|
|
|
/**
|
|
* @param Clock $clock Clock für Timestamp-Generierung
|
|
* @param LogLevel $minLevel Minimales Level, das geloggt werden soll
|
|
* @param array<LogHandler> $handlers Array von Log-Handlern
|
|
* @param ProcessorManager $processorManager Processor Manager für die Verarbeitung
|
|
* @param LogContextManager|null $contextManager Optional: Context Manager für automatische Kontext-Anreicherung
|
|
*/
|
|
public function __construct(
|
|
private Clock $clock,
|
|
private LogLevel $minLevel = LogLevel::DEBUG,
|
|
/** @var LogHandler[] */
|
|
private array $handlers = [],
|
|
private ProcessorManager $processorManager = new ProcessorManager(),
|
|
private ?LogContextManager $contextManager = null,
|
|
) {
|
|
// Channel-Logger-Registry initialisieren
|
|
$this->channelRegistry = new ChannelLoggerRegistry($this);
|
|
}
|
|
|
|
public function debug(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::DEBUG, $message, $context);
|
|
}
|
|
|
|
public function info(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::INFO, $message, $context);
|
|
}
|
|
|
|
public function notice(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::NOTICE, $message, $context);
|
|
}
|
|
|
|
public function warning(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::WARNING, $message, $context);
|
|
}
|
|
|
|
public function error(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::ERROR, $message, $context);
|
|
}
|
|
|
|
public function critical(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::CRITICAL, $message, $context);
|
|
}
|
|
|
|
public function alert(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::ALERT, $message, $context);
|
|
}
|
|
|
|
public function emergency(string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->log(LogLevel::EMERGENCY, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Log-Nachricht mit beliebigem Level erstellen
|
|
*
|
|
* @param LogLevel $level Log-Level
|
|
* @param string $message Log-Nachricht
|
|
* @param LogContext|null $context Strukturierter LogContext
|
|
* @throws DateMalformedStringException
|
|
*/
|
|
public function log(LogLevel $level, string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->createAndProcessRecord($level, $message, $context);
|
|
}
|
|
|
|
/**
|
|
* Loggt in einen spezifischen Channel
|
|
*
|
|
* @internal Wird von ChannelLogger verwendet
|
|
*/
|
|
public function logToChannel(LogChannel $channel, LogLevel $level, string $message, ?LogContext $context = null): void
|
|
{
|
|
$this->createAndProcessRecord($level, $message, $context, $channel->value);
|
|
}
|
|
|
|
/**
|
|
* Erstellt und verarbeitet einen Log-Record
|
|
*
|
|
* @param LogLevel $level Log-Level
|
|
* @param string $message Log-Nachricht
|
|
* @param LogContext|null $context Strukturierter LogContext
|
|
* @param string|null $channel Optional: Channel-Name
|
|
* @throws DateMalformedStringException
|
|
*/
|
|
private function createAndProcessRecord(LogLevel $level, string $message, ?LogContext $context = null, ?string $channel = null): void
|
|
{
|
|
// Wenn kein Context übergeben, leeren Context erstellen
|
|
if ($context === null) {
|
|
$context = LogContext::empty();
|
|
}
|
|
|
|
// Prüfen ob Level hoch genug ist
|
|
if ($level->isLowerThan($this->minLevel)) {
|
|
return;
|
|
}
|
|
|
|
// LogContext automatisch mit aktuellem Context anreichern
|
|
$finalContext = $this->enrichWithCurrentContext($context);
|
|
|
|
// Log-Record erstellen
|
|
$record = new LogRecord(
|
|
message: $message,
|
|
context: $finalContext,
|
|
level: $level,
|
|
timestamp: $this->clock->now(),
|
|
channel: $channel
|
|
);
|
|
|
|
// Record durch alle Processors verarbeiten
|
|
$processedRecord = $this->processorManager->processRecord($record);
|
|
|
|
// Alle Handler durchlaufen
|
|
foreach ($this->handlers as $handler) {
|
|
if ($handler->isHandling($processedRecord)) {
|
|
|
|
$array = explode('\\',$handler::class);
|
|
$handlerName = end($array);
|
|
|
|
// Log-Record erstellen
|
|
$record = new LogRecord(
|
|
message: $handlerName . ' --- ' . $message,
|
|
context: $finalContext,
|
|
level: $level,
|
|
timestamp: $this->clock->now(),
|
|
channel: $channel
|
|
);
|
|
$processedRecord = $this->processorManager->processRecord($record);
|
|
|
|
$handler->handle($processedRecord);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reichert den übergebenen Context mit dem aktuellen Context vom LogContextManager an
|
|
*/
|
|
private function enrichWithCurrentContext(LogContext $context): LogContext
|
|
{
|
|
// Wenn kein ContextManager verfügbar ist, Context unverändert zurückgeben
|
|
if ($this->contextManager === null) {
|
|
return $context;
|
|
}
|
|
|
|
$currentContext = $this->contextManager->getCurrentContext();
|
|
|
|
// Wenn der übergebene Context ein LogContext ist, mit aktuellem Context mergen
|
|
return $currentContext->merge($context);
|
|
}
|
|
|
|
|
|
/**
|
|
* Holt einen ChannelLogger für einen spezifischen Channel
|
|
*/
|
|
public function channel(LogChannel|string $channel): Logger&HasChannel
|
|
{
|
|
return $this->channelRegistry->get($channel);
|
|
}
|
|
|
|
/**
|
|
* Gibt die aktuelle Konfiguration des Loggers zurück
|
|
*/
|
|
public function getConfiguration(): array
|
|
{
|
|
return [
|
|
'minLevel' => $this->minLevel->value,
|
|
'handlers' => array_map(fn (LogHandler $h) => get_class($h), $this->handlers),
|
|
'processors' => $this->processorManager->getProcessorList(),
|
|
'registeredChannels' => $this->channelRegistry->getRegisteredChannels(),
|
|
];
|
|
}
|
|
}
|