refactor: improve logging system and add deployment fixes

- Enhance logging handlers (Console, DockerJson, File, JsonFile, MultiFile)
- Improve exception and line formatters
- Update logger initialization and processor management
- Add Ansible playbooks for staging 502 error troubleshooting
- Update deployment documentation
- Fix serializer and queue components
- Update error kernel and queued log handler
This commit is contained in:
2025-11-02 01:37:49 +01:00
parent 2defdf2baf
commit cf0ad6e905
23 changed files with 612 additions and 556 deletions

View File

@@ -5,10 +5,9 @@ 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;
use DateTimeImmutable;
use DateTimeZone;
/**
* Einfacher Logger für das Framework.
@@ -19,12 +18,14 @@ 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 = [],
@@ -84,6 +85,30 @@ final readonly class DefaultLogger implements Logger, SupportsChannels
* @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) {
@@ -103,47 +128,8 @@ final readonly class DefaultLogger implements Logger, SupportsChannels
message: $message,
context: $finalContext,
level: $level,
timestamp: new DateTimeImmutable(timezone: new DateTimeZone('Europe/Berlin')),
);
// Record durch alle Processors verarbeiten
$processedRecord = $this->processorManager->processRecord($record);
// Alle Handler durchlaufen
foreach ($this->handlers as $handler) {
if ($handler->isHandling($processedRecord)) {
$handler->handle($processedRecord);
}
}
}
/**
* Loggt in einen spezifischen Channel
*
* @internal Wird von ChannelLogger verwendet
*/
public function logToChannel(LogChannel $channel, LogLevel $level, string $message, ?LogContext $context = 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 mit Channel
$record = new LogRecord(
message: $message,
context: $finalContext,
level: $level,
timestamp: new DateTimeImmutable(timezone: new DateTimeZone('Europe/Berlin')),
channel: $channel->value
timestamp: $this->clock->now(),
channel: $channel
);
// Record durch alle Processors verarbeiten
@@ -173,75 +159,6 @@ final readonly class DefaultLogger implements Logger, SupportsChannels
return $currentContext->merge($context);
}
/**
* Konvertiert LogContext zu Array für Legacy-Kompatibilität
* @deprecated
*/
private function convertLogContextToArray(LogContext $logContext): array
{
$context = $logContext->structured;
// Tags hinzufügen
if ($logContext->hasTags()) {
$context['_tags'] = $logContext->tags;
}
// Trace-Informationen hinzufügen
if ($logContext->trace !== null) {
$context['_trace_id'] = $logContext->trace->getTraceId();
if ($activeSpan = $logContext->trace->getActiveSpan()) {
$context['_span_id'] = $activeSpan->spanId;
}
}
// User-Kontext hinzufügen
if ($logContext->user !== null) {
$context['_user_id'] = $logContext->user->userId ?? null;
}
// Request-Kontext hinzufügen
if ($logContext->request !== null) {
$context['_request_id'] = $logContext->request->requestId ?? null;
}
return array_merge($context, $logContext->metadata);
}
/**
* Reichert LogRecord mit strukturierten Daten aus LogContext an
*/
private function enrichRecordWithLogContext(LogRecord $record, LogContext $logContext): LogRecord
{
// Tags als Extra hinzufügen
if ($logContext->hasTags()) {
$record->addExtra('structured_tags', $logContext->tags);
}
// Trace-Kontext als Extra hinzufügen
if ($logContext->trace !== null) {
$record->addExtra('trace_context', [
'trace_id' => $logContext->trace->getTraceId(),
'active_span' => $logContext->trace->getActiveSpan()?->toArray(),
]);
}
// User-Kontext als Extra hinzufügen
if ($logContext->user !== null) {
$record->addExtra('user_context', $logContext->user->toArray());
}
// Request-Kontext als Extra hinzufügen
if ($logContext->request !== null) {
$record->addExtra('request_context', $logContext->request->toArray());
}
// Metadaten als Extra hinzufügen
if (! empty($logContext->metadata)) {
$record->addExtras($logContext->metadata);
}
return $record;
}
/**
* Holt einen ChannelLogger für einen spezifischen Channel