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:
@@ -6,8 +6,9 @@ namespace App\Framework\Logging\Handlers;
|
||||
|
||||
use App\Framework\Config\Environment;
|
||||
use App\Framework\Core\PathProvider;
|
||||
use App\Framework\Filesystem\Serializers\JsonSerializer;
|
||||
use App\Framework\Logging\LogHandler;
|
||||
use App\Framework\Logging\Formatter\JsonFormatter;
|
||||
use App\Framework\Logging\Formatter\LogFormatter;
|
||||
use App\Framework\Logging\LogLevel;
|
||||
use App\Framework\Logging\LogRecord;
|
||||
|
||||
@@ -15,7 +16,7 @@ use App\Framework\Logging\LogRecord;
|
||||
* Handler für die Ausgabe von Log-Einträgen als JSON in Dateien.
|
||||
* Besonders nützlich für maschinelle Verarbeitung und Log-Aggregatoren.
|
||||
*
|
||||
* Nutzt JsonSerializer für einheitliche JSON-Ausgabe konsistent mit JsonFormatter.
|
||||
* Nutzt JsonFormatter für einheitliche JSON-Ausgabe.
|
||||
*
|
||||
* Standard-Felder für Log-Aggregatoren:
|
||||
* - @timestamp: Elasticsearch-konformes Zeitstempelfeld
|
||||
@@ -37,62 +38,31 @@ class JsonFileHandler implements LogHandler
|
||||
private string $logFile;
|
||||
|
||||
/**
|
||||
* @var array Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen
|
||||
* @var JsonFormatter Formatter für die JSON-Ausgabe
|
||||
*/
|
||||
private array $includedFields;
|
||||
private JsonFormatter $formatter;
|
||||
|
||||
/**
|
||||
* @var PathProvider|null PathProvider für die Auflösung von Pfaden
|
||||
*/
|
||||
private ?PathProvider $pathProvider = null;
|
||||
|
||||
/**
|
||||
* @var JsonSerializer JSON Serializer für konsistente Ausgabe
|
||||
*/
|
||||
private JsonSerializer $serializer;
|
||||
|
||||
/**
|
||||
* @var bool Flatten LogContext für bessere Aggregator-Kompatibilität
|
||||
*/
|
||||
private bool $flattenContext;
|
||||
|
||||
/**
|
||||
* @var string Deployment-Umgebung (production, staging, development)
|
||||
*/
|
||||
private string $environment;
|
||||
|
||||
/**
|
||||
* @var string Server-Hostname
|
||||
*/
|
||||
private string $host;
|
||||
|
||||
/**
|
||||
* @var string Service-/Anwendungsname
|
||||
*/
|
||||
private string $serviceName;
|
||||
|
||||
/**
|
||||
* Erstellt einen neuen JsonFileHandler
|
||||
*
|
||||
* @param JsonFormatter $formatter Formatter für die JSON-Ausgabe
|
||||
* @param string $logFile Pfad zur Log-Datei
|
||||
* @param LogLevel|int $minLevel Minimales Level, ab dem dieser Handler aktiv wird
|
||||
* @param array|null $includedFields Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen (null = alle)
|
||||
* @param PathProvider|null $pathProvider Optional: PathProvider für die Auflösung von Pfaden
|
||||
* @param bool $flattenContext Flatten LogContext structured data (default: true)
|
||||
* @param Environment|null $env Optional: Environment für Konfiguration
|
||||
* @param string|null $serviceName Optional: Service-/Anwendungsname
|
||||
*/
|
||||
public function __construct(
|
||||
JsonFormatter $formatter,
|
||||
string $logFile,
|
||||
LogLevel|int $minLevel = LogLevel::INFO,
|
||||
?array $includedFields = null,
|
||||
?PathProvider $pathProvider = null,
|
||||
bool $flattenContext = true,
|
||||
?Environment $env = null,
|
||||
?string $serviceName = null
|
||||
?PathProvider $pathProvider = null
|
||||
) {
|
||||
$this->pathProvider = $pathProvider;
|
||||
$this->flattenContext = $flattenContext;
|
||||
$this->formatter = $formatter;
|
||||
|
||||
// Pfad auflösen, falls PathProvider vorhanden
|
||||
if ($this->pathProvider !== null && ! str_starts_with($logFile, '/')) {
|
||||
@@ -102,34 +72,6 @@ class JsonFileHandler implements LogHandler
|
||||
$this->logFile = $logFile;
|
||||
$this->minLevel = $minLevel instanceof LogLevel ? $minLevel : LogLevel::fromValue($minLevel);
|
||||
|
||||
// Standardfelder, falls nicht anders angegeben (konsistent mit JsonFormatter)
|
||||
$this->includedFields = $includedFields ?? [
|
||||
'timestamp',
|
||||
'@timestamp',
|
||||
'level',
|
||||
'level_value',
|
||||
'severity',
|
||||
'channel',
|
||||
'message',
|
||||
'environment',
|
||||
'host',
|
||||
'service',
|
||||
'context',
|
||||
'extra',
|
||||
];
|
||||
|
||||
// Compact JSON für Datei-Ausgabe (eine Zeile pro Log)
|
||||
$this->serializer = JsonSerializer::compact();
|
||||
|
||||
// Environment Detection (production, staging, development)
|
||||
$this->environment = $env?->getString('APP_ENV', 'production') ?? 'production';
|
||||
|
||||
// Host Detection
|
||||
$this->host = gethostname() ?: 'unknown';
|
||||
|
||||
// Service Name (default: app name from env)
|
||||
$this->serviceName = $serviceName ?? $env?->getString('APP_NAME', 'app') ?? 'app';
|
||||
|
||||
// Stelle sicher, dass das Verzeichnis existiert
|
||||
$this->ensureDirectoryExists(dirname($logFile));
|
||||
}
|
||||
@@ -147,81 +89,26 @@ class JsonFileHandler implements LogHandler
|
||||
*/
|
||||
public function handle(LogRecord $record): void
|
||||
{
|
||||
// Formatiere Record zu einheitlichem Array (konsistent mit JsonFormatter)
|
||||
$data = $this->formatRecord($record);
|
||||
// Formatter verwenden für JSON-Formatierung
|
||||
$json = ($this->formatter)($record);
|
||||
|
||||
// Als JSON formatieren mit JsonSerializer und in die Datei schreiben
|
||||
$json = $this->serializer->serialize($data) . PHP_EOL;
|
||||
$this->write($json);
|
||||
// JsonFormatter gibt immer string zurück
|
||||
$output = is_string($json) ? $json : json_encode($json, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
|
||||
// Stelle sicher, dass ein Newline vorhanden ist
|
||||
if (!str_ends_with($output, PHP_EOL)) {
|
||||
$output .= PHP_EOL;
|
||||
}
|
||||
|
||||
$this->write($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatiert LogRecord zu einheitlichem Array für JSON-Serialisierung
|
||||
* (Gleiche Logik wie JsonFormatter für Konsistenz)
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
* Gibt den Formatter zurück
|
||||
*/
|
||||
private function formatRecord(LogRecord $record): array
|
||||
public function getFormatter(): LogFormatter
|
||||
{
|
||||
$timestamp = $record->timestamp->format('c'); // ISO 8601
|
||||
|
||||
$data = [
|
||||
// Standard Log Fields
|
||||
'timestamp' => $timestamp,
|
||||
'@timestamp' => $timestamp, // Elasticsearch convention
|
||||
'level' => $record->level->getName(),
|
||||
'level_value' => $record->level->value,
|
||||
'severity' => $record->level->toRFC5424(), // RFC 5424 (0-7)
|
||||
'channel' => $record->channel,
|
||||
'message' => $record->message,
|
||||
|
||||
// Infrastructure Fields (for log aggregators)
|
||||
'environment' => $this->environment,
|
||||
'host' => $this->host,
|
||||
'service' => $this->serviceName,
|
||||
];
|
||||
|
||||
// Context hinzufügen mit optionalem Flatten
|
||||
$context = $record->context->toArray();
|
||||
|
||||
if ($this->flattenContext && isset($context['structured'])) {
|
||||
// Flatten: Nur strukturierte Daten für bessere Aggregator-Kompatibilität
|
||||
$data['context'] = $context['structured'];
|
||||
} else {
|
||||
// Raw: Gesamtes LogContext-Array
|
||||
$data['context'] = $context;
|
||||
}
|
||||
|
||||
// Extras hinzufügen (wenn vorhanden)
|
||||
if (!empty($record->extra)) {
|
||||
$data['extra'] = $record->extra;
|
||||
}
|
||||
|
||||
// Nur gewünschte Felder behalten, in Reihenfolge von $includedFields
|
||||
return $this->filterFields($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert Array nach includedFields
|
||||
*
|
||||
* @param array<string, mixed> $data
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function filterFields(array $data): array
|
||||
{
|
||||
if (empty($this->includedFields)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
$filtered = [];
|
||||
|
||||
foreach ($this->includedFields as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$filtered[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
return $this->formatter;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,15 +139,6 @@ class JsonFileHandler implements LogHandler
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setzt die Liste der Felder, die in der JSON-Ausgabe enthalten sein sollen
|
||||
*/
|
||||
public function setIncludedFields(array $fields): self
|
||||
{
|
||||
$this->includedFields = $fields;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log-Datei setzen
|
||||
|
||||
Reference in New Issue
Block a user