refactor: documents
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
93
core/lib/Logger/TenantAwareLogger.php
Normal file
93
core/lib/Logger/TenantAwareLogger.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace KTXC\Logger;
|
||||
|
||||
use KTXC\SessionTenant;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* PSR-3 decorator with two responsibilities:
|
||||
*
|
||||
* 1. Tenant-id injection — every log record is enriched with a `tenant` field.
|
||||
* When a tenant session is active the real tenant identifier is used; otherwise
|
||||
* the value is "system" (boot phase, CLI, bad-domain rejections, etc.).
|
||||
* The id is passed to inner loggers via the reserved `__tenant` context key,
|
||||
* which each concrete logger (FileLogger, SystemdLogger, SyslogLogger) extracts
|
||||
* and renders as a top-level field, then removes from context before output.
|
||||
*
|
||||
* 2. Per-tenant file routing (optional, controlled by $perTenant) — when enabled,
|
||||
* writes for an active tenant are routed to:
|
||||
* {logDir}/tenant/{tenantIdentifier}/{channel}.jsonl
|
||||
* Messages that carry tenant "system" always go to the global logger.
|
||||
*
|
||||
* Both behaviours rely on a live SessionTenant reference that is populated lazily
|
||||
* by TenantMiddleware — the same pattern the cache stores use in Kernel::configureContainer().
|
||||
*/
|
||||
class TenantAwareLogger implements LoggerInterface
|
||||
{
|
||||
/** @var array<string, LoggerInterface> Per-tenant logger cache, keyed by tenant identifier. */
|
||||
private array $tenantLoggers = [];
|
||||
|
||||
/**
|
||||
* @param LoggerInterface $globalLogger Fallback logger (also used when perTenant = false).
|
||||
* @param SessionTenant $sessionTenant Live reference configured by TenantMiddleware.
|
||||
* @param string $logDir Base log directory (e.g. /var/www/app/var/log).
|
||||
* @param string $channel Log file basename (e.g. 'app' → app.jsonl).
|
||||
* @param string $minLevel Minimum PSR-3 level for lazily-created per-tenant loggers.
|
||||
* @param bool $perTenant When true, route tenant writes to per-tenant files.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly LoggerInterface $globalLogger,
|
||||
private readonly SessionTenant $sessionTenant,
|
||||
private readonly string $logDir,
|
||||
private readonly string $channel = 'app',
|
||||
private readonly string $minLevel = 'debug',
|
||||
private readonly bool $perTenant = false,
|
||||
) {
|
||||
LogLevelSeverity::validate($minLevel);
|
||||
}
|
||||
|
||||
public function emergency($message, array $context = []): void { $this->log('emergency', $message, $context); }
|
||||
public function alert($message, array $context = []): void { $this->log('alert', $message, $context); }
|
||||
public function critical($message, array $context = []): void { $this->log('critical', $message, $context); }
|
||||
public function error($message, array $context = []): void { $this->log('error', $message, $context); }
|
||||
public function warning($message, array $context = []): void { $this->log('warning', $message, $context); }
|
||||
public function notice($message, array $context = []): void { $this->log('notice', $message, $context); }
|
||||
public function info($message, array $context = []): void { $this->log('info', $message, $context); }
|
||||
public function debug($message, array $context = []): void { $this->log('debug', $message, $context); }
|
||||
|
||||
public function log($level, $message, array $context = []): void
|
||||
{
|
||||
// Resolve current tenant id; fall back to 'system' for CLI / boot phase.
|
||||
$tenantId = 'system';
|
||||
if ($this->sessionTenant->configured()) {
|
||||
$tenantId = $this->sessionTenant->identifier() ?? 'system';
|
||||
}
|
||||
|
||||
// Inject tenant id as a reserved context key that concrete loggers extract.
|
||||
$context['__tenant'] = $tenantId;
|
||||
|
||||
$this->resolveLogger($tenantId)->log($level, $message, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the logger to write to for the given tenant.
|
||||
* When per-tenant routing is disabled (or tenant is "system"), always returns the global logger.
|
||||
*/
|
||||
private function resolveLogger(string $tenantId): LoggerInterface
|
||||
{
|
||||
if (!$this->perTenant || $tenantId === 'system') {
|
||||
return $this->globalLogger;
|
||||
}
|
||||
|
||||
if (!isset($this->tenantLoggers[$tenantId])) {
|
||||
$tenantLogDir = rtrim($this->logDir, '/') . '/tenant/' . $tenantId;
|
||||
$this->tenantLoggers[$tenantId] = new LevelFilterLogger(
|
||||
new FileLogger($tenantLogDir, $this->channel),
|
||||
$this->minLevel,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->tenantLoggers[$tenantId];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user