diff --git a/src/Tracy/Debugger.php b/src/Tracy/Debugger.php index 7be8e1770..b5e0d44df 100644 --- a/src/Tracy/Debugger.php +++ b/src/Tracy/Debugger.php @@ -138,12 +138,12 @@ final public function __construct() /** * Enables displaying or logging errors and exceptions. - * @param mixed production, development mode, autodetection or IP address(es) whitelist. - * @param string error log directory - * @param string administrator email; enables email sending in production mode + * @param mixed production, development mode, autodetection or IP address(es) whitelist. + * @param string|ILogger error log directory or logger instance + * @param string administrator email; enables email sending in production mode * @return void */ - public static function enable($mode = null, $logDirectory = null, $email = null) + public static function enable($mode = null, $logDirectoryOrLogger = null, $email = null) { if ($mode !== null || self::$productionMode === null) { self::$productionMode = is_bool($mode) ? $mode : !self::detectDebugMode($mode); @@ -159,8 +159,10 @@ public static function enable($mode = null, $logDirectory = null, $email = null) if ($email !== null) { self::$email = $email; } - if ($logDirectory !== null) { - self::$logDirectory = $logDirectory; + if (is_string($logDirectoryOrLogger)) { + self::$logDirectory = $logDirectoryOrLogger; + } elseif ($logDirectoryOrLogger instanceof ILogger) { + self::$logger = $logDirectoryOrLogger; } if (self::$logDirectory) { if (!preg_match('#([a-z]+:)?[/\\\\]#Ai', self::$logDirectory)) { diff --git a/src/Tracy/StreamLogger.php b/src/Tracy/StreamLogger.php new file mode 100644 index 000000000..db0f2cf59 --- /dev/null +++ b/src/Tracy/StreamLogger.php @@ -0,0 +1,74 @@ +path = $path; + } + + + public function log($message, $priority = self::INFO) + { + $line = $this->formatLogLine($message); + if (!@file_put_contents($this->path, $line . PHP_EOL, FILE_APPEND | LOCK_EX)) { // @ is escalated to exception + throw new \RuntimeException("Unable to write to log file '{$this->path}'. Is directory writable?"); + } + } + + + /** + * @param string|\Exception|\Throwable + * @return string + */ + protected function formatLogLine($message) + { + return implode(' ', [ + @date('[Y-m-d H-i-s]'), // @ timezone may not be set + preg_replace('#\s*\r?\n\s*#', ' ', $this->formatMessage($message)), + ' @ ' . Helpers::getSource(), + ]); + } + + + /** + * @param string|\Exception|\Throwable + * @return string + */ + protected function formatMessage($message) + { + if ($message instanceof \Exception || $message instanceof \Throwable) { + while ($message) { + $tmp[] = ($message instanceof \ErrorException + ? Helpers::errorTypeToString($message->getSeverity()) . ': ' . $message->getMessage() + : Helpers::getClass($message) . ': ' . $message->getMessage() . ($message->getCode() ? ' #' . $message->getCode() : '') + ) . ' in ' . $message->getFile() . ':' . $message->getLine(); + $message = $message->getPrevious(); + } + $message = implode("\ncaused by ", $tmp); + + } elseif (!is_string($message)) { + $message = Dumper::toText($message); + } + + return trim($message); + } +}