logFile = rtrim($logDir, '/') . '/' . $channel . '.log'; } 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 { $dt = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6F', microtime(true))); $timestamp = $dt?->format('Y-m-d H:i:s.u') ?? date('Y-m-d H:i:s'); $line = $timestamp . ' ' . $this->interpolate((string) $message, $context) . PHP_EOL; @file_put_contents($this->logFile, $line, FILE_APPEND | LOCK_EX); } private function interpolate(string $message, array $context): string { if (!str_contains($message, '{')) { return $message; } $replace = []; foreach ($context as $key => $val) { if (is_array($val) || (is_object($val) && !method_exists($val, '__toString'))) { continue; } $replace['{' . $key . '}'] = (string) $val; } return strtr($message, $replace); } }