JsonLineLogger.php
Pfad: src/Infrastructure/Rechnung/JsonLineLogger.php
Ext: php
Größe: 2908 Bytes
Geändert: 2026-07-16 17:37:59+02
<?php
declare(strict_types=1);
namespace Demo\Infrastructure\Rechnung;
use Demo\Domain\Rechnung\InvoiceLoggerPort;
use RuntimeException;
final class JsonLineLogger implements InvoiceLoggerPort
{
public function __construct(private readonly string $logPath)
{
if ($this->logPath === '') {
throw new RuntimeException('Logpfad fehlt');
}
}
/** @param array<string,mixed> $context */
public function logStep(string $runId, string $step, string $status, array $context = []): void
{
$record = [
'timestamp' => gmdate('c'),
'runId' => $runId,
'step' => $step,
'status' => $status,
'context' => $this->redact($context),
];
$line = json_encode($record, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR) . "\n";
$handle = @fopen($this->logPath, 'ab');
if ($handle === false) {
throw new RuntimeException('Auditlog kann nicht geoeffnet werden');
}
try {
if (!flock($handle, LOCK_EX)) {
throw new RuntimeException('Auditlog kann nicht gesperrt werden');
}
$remaining = $line;
while ($remaining !== '') {
$written = @fwrite($handle, $remaining);
if ($written === false || $written === 0) {
throw new RuntimeException('Auditlog kann nicht geschrieben werden');
}
$remaining = substr($remaining, $written);
}
if (!fflush($handle)) {
throw new RuntimeException('Auditlog kann nicht persistiert werden');
}
if (function_exists('fsync') && !fsync($handle)) {
throw new RuntimeException('Auditlog kann nicht synchronisiert werden');
}
} finally {
flock($handle, LOCK_UN);
fclose($handle);
}
}
/**
* @param array<string,mixed> $values
* @return array<string,mixed>
*/
private function redact(array $values): array
{
$redacted = [];
foreach ($values as $key => $value) {
if (preg_match('/password|secret|token|authorization|api.?key|cookie/i', $key) === 1) {
$redacted[$key] = '[REDACTED]';
continue;
}
if (is_array($value)) {
$redacted[$key] = $this->redact($value);
continue;
}
$redacted[$key] = is_string($value) ? $this->redactText($value) : $value;
}
return $redacted;
}
private function redactText(string $value): string
{
$value = preg_replace('/Bearer\s+[A-Za-z0-9._~+\/-]+/i', 'Bearer [REDACTED]', $value) ?? $value;
return preg_replace('#(https?://)[^/@\s]+:[^/@\s]+@#i', '$1[REDACTED]@', $value) ?? $value;
}
}
Frühere Versionen
| Version | Zeitpunkt | Operation |
|---|---|---|
| 42 | 2026-07-16 19:55:01.889455+02 | UPDATE |