БлогNot. Простой класс логирования на PHP

Простой класс логирования на PHP

Удобный текстовый лог, куда можно писать сообщения от скриптов, требуется разработчику довольно часто. Простой логгер из этой заметки реализован одним небольшим классом на 120 строк кода, легко интегрируется в проекты без необходимости создавать экземпляры класса, например,

<?php
 include_once('log.php');

 function div ($a, $b) {
  if (!$b) {
   throw new Exception('Division by zero');
  }
  return $a/$b;
 }

 try {
  $result = div (4, 0);
 } 
 catch (Exception $e) {
  log::warning("Деление на ноль", [$e->getMessage()]); //сообщение в лог-файл
  echo "Деление на ноль<br>"; //сообщение в браузер
  $result = 0;
 }
 if (!file_exists('my_file.txt')) {
  log::fatal("Файл отсутствует"); //сообщение в лог-файл
  echo "Нет файла с my_file.txt, скрипт остановлен<br>"; //сообщение в браузер
 }
?>

В лог-файле будет записано:

[17:56:22 05.06.2021] [localhost/my/log/index.php] [15] : [WARNING] - Деление на ноль ["Division by zero"]
[17:56:22 05.06.2021] [localhost/my/log/index.php] [20] : [FATAL] - Файл отсутствует 

Логи создаются и поддерживаются во вложенной папке logs, файлы логов создаются посуточно и имеют шаблон имени вида log_05062021.txt

Поддерживается 6 уровней сообщений:

  • Информация - INFO
  • Уведомление - NOTICE
  • Отладка - DEBUG
  • Предупреждение - WARNING
  • Ошибка - ERROR
  • Фатальная ошибка - FATAL

Формат файлов лога пригоден для дальнейшей алгоритмической обработки. Ниже прилагается исходник файла log.php, проверенный на локальном хосте XAMPP с PHP 8.

<?php
class log {
 protected static $log_file;
 protected static $file;
 protected static $options = [
  'dateFormat' => 'dmY', //формат даты для имени файла
  'logFormat' => 'H:i:s d.m.Y' //формат даты и времени для записи лога
 ];
 private static $instance;

 public static function createLogFile() { //Создать/открыть файл
  $time = date(static::$options['dateFormat']);
  static::$log_file =  __DIR__ . "/logs/log_{$time}.txt";
  if (!file_exists(__DIR__ . '/logs')) {
   mkdir(__DIR__ . '/logs', 0777, true);
  }
  if (!file_exists(static::$log_file)) {
   fopen(static::$log_file, 'w') or exit("Не могу создать {static::log_file}!");
  }
  if (!is_writable(static::$log_file)) {
   throw new Exception("Не могу записать в {static::$log_file}, проверьте права", 1);
  }
 }

 public static function setOptions ($options = []) { //Установить опции
  static::$options = array_merge(static::$options, $options);
 }

 public static function info ($message, array $context = []) { //Информация
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'INFO', 'context' => $context
  ]);
 }

 public static function notice ($message, array $context = [])  { //Уведомление
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'NOTICE', 'context' => $context
  ]);
 }

 public static function debug ($message, array $context = []) { //Отладка
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'DEBUG', 'context' => $context
  ]);
 }

 public static function warning ($message, array $context = []) { //Предупреждение
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'WARNING', 'context' => $context
  ]);
 }

 public static function error ($message, array $context = []) { //Ошибка
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'ERROR', 'context' => $context
  ]);
 }

 public static function fatal ($message, array $context = []) { //Фатальная ошибка
  $backtrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1);
  static::writeLog([
   'message' => $message, 'backtrace' => $backtrace, 'level' => 'FATAL', 'context' => $context
  ]);
 }

 public static function writeLog ($args = []) { //Запись в лог
  static::createLogFile();
  if (!is_resource(static::$file)) {
   static::openLog();
  }
  $time = date(static::$options['logFormat']);
  $context = json_encode ($args['context']);
  $caller = array_shift ($args['backtrace']);
  $btLine = $caller['line'];
  $btPath = $caller['file'];
  $path = static::absToRelPath($btPath);
  $timeLog = is_null($time) ? "[N/A] " : "[{$time}] ";
  $pathLog = is_null($path) ? "[N/A] " : "[{$path}] ";
  $lineLog = is_null($btLine) ? "[N/A] " : "[{$btLine}] ";
  $levelLog = is_null($args['level']) ? "[N/A]" : "[{$args['level']}]";
  $messageLog = is_null($args['message']) ? "N/A" : "{$args['message']}";
  $contextLog = empty($args['context']) ? "" : "{$context}";
  fwrite(static::$file, "{$timeLog}{$pathLog}{$lineLog}: {$levelLog} - {$messageLog} {$contextLog}" . PHP_EOL);
  static::closeFile();
 }

 private static function openLog() { //Открыть файл лога
  $openFile = static::$log_file;
  static::$file = fopen ($openFile, 'a') or exit ("Не могу открыть $openFile!");
 }

 public static function closeFile() { //Закрыть файл лога
  if (static::$file) {
   fclose (static::$file);
  }
 }

 public static function absToRelPath ($pathToConvert) { //Абсолютный путь в относительный URL
  $pathAbs = str_replace(['/', '\\'], '/', $pathToConvert);
  $documentRoot = str_replace(['/', '\\'], '/', $_SERVER['DOCUMENT_ROOT']);
  return $_SERVER['SERVER_NAME'] . str_replace($documentRoot, '', $pathAbs);
 }

 //Остальное просто чтобы класс был "полноценным"
 protected function __construct() {}
 protected function __clone() {}
 public function __wakeup() {
  throw new \Exception("Cannot unserialize a class");
 }
 public static function getInstance() {
  if (is_null(self::$instance)) { self::$instance = new self(); }
  return self::$instance;
 }
 private function __destruct() {}
}
?>

05.06.2021, 22:58 [1945 просмотров]


теги: textprocessing программирование ошибка список php

К этой статье пока нет комментариев, Ваш будет первым