Для логгирования иногда важно не только записать какое то сообщение, но также указать — где именно произошло событие в скрипте. Давайте посмотрим, как можно получить номер строки и имя файла в PHP.
Данная информация может быть предоставлена php функцией debug_backtrace(), которая предоставляет информацию о состоянии стека вызова функций в виде массива.
Пример массива, который возвращает debug_backtrace():
1 2 3 4 5 6 7 8 9 10 |
Array ( [0] => Array ( [file] => D:\OpenServer.526\domains\test.loc\web\debug_backtrace.php [line] => 8 [function] => getInfo [args] => Array() ) ) |
Здесь есть имя файла и номер строки, где была вызвана некая функция getInfo.
Оформим нашу идею в виде функции, которая возвращает описание места в скрипте, откуда она была вызвана в программе:
1 2 3 4 5 6 7 8 |
function getInfo() { $backtrace = debug_backtrace(); $fileinfo = ''; if (!empty($backtrace[0]) && is_array($backtrace[0])) { $fileinfo = $backtrace[0]['file'] . ":" . $backtrace[0]['line']; } return $fileinfo; } |
Т.е. если мы вызовем её где то в нашей программе, она вернет нам имя файла и номер строки.
Если вызов функции используется в обертке другой функции, к примеру той, что пишет сообщение в лог файл, то нужно посмотреть глубже в стек, чтобы получить требуемые данные.
Виртуальный лог
Оформим эту идею в виде виртуального класса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class VirtualLog { public function error($error) { $this->log($error->getMessage() . ' /' . $error->getFile() . ":" . $error->getLine(), 'error'); } public function message($messageText) { $this->log($messageText . ' /' . self::getInfo(1), 'info'); } private static function getInfo($level = 0) { $bt = debug_backtrace(); $fileinfo = 'Unknown place:'; if (!empty($bt[$level]) && is_array($bt[$level])) { $fileinfo = $bt[$level]['file'] . ":" . $bt[$level]['line']; } return $fileinfo; } public function log($message, $message_type) { print "$message_type : $message \n"; } } |
Основная функция класса — log выводит сообщение просто на экран, поэтому я назвал класс виртуальным. А чтобы сообщение сохранялось в базу или в файл — требуется переписать эту функцию в дочернем классе.
Вторая особенность — функция log не добавляет имя файла и номер строки в сообщение, сохраняя возможность прямого вывода. Вместо этого реализованы еще два метода, которые это делают. Один из них error, принимает на входе объект Exception и использует его данные. А второй — message — использует уже знакомую функцию getInfo.
Пользоваться классом просто:
1 2 |
$virtualLog = new VirtualLog(); $virtualLog->message('Text message'); |
Скрипт выведет что то вроде следующего:
1 2 |
info : Text message /D:\OpenServer.526\domains\test.loc\web\debug_backtrace.php:42 |
Логгирование в файл
Т.к. в консоль писать не очень интересно на практике, то давайте сделаем сохранение данных в файл. Для этого создадим дочерний класс, реализующий возможность записи лога в указанный файл.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class FileLog extends VirtualLog { var $logFilename = null; function __construct($logFilename) { $this->logFilename = $logFilename; } public function log($message, $message_type = 'info') { file_put_contents($this->logFilename, date('Y-m-d H:i:s') . " $message_type : $message \n", FILE_APPEND); } } |
При инициализации класса нужно указать имя файла, а дальше использовать всё те же методы:
1 2 |
$fileLog = new FileLog('mylogfile.log'); $fileLog->message('Text message'); |
Ну и пример с обработкой исключения:
1 2 3 4 5 6 |
try { throw new Exception('My error message!'); } catch (Exception $ex) { $fileLog->error($ex); } |