Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Klasa logująca komunikaty wyjątków do pliku - LoggedException
Forum PHP.pl > Forum > PHP > Object-oriented programming
likemandrake
Witam

Napisalem klase do obslugi wyjątków, która przechwycone komunikaty, kod błędu i ślad wywołań zapisuje do pliku.

Prosiłbym o komentarze, co możnaby zrobić lepiej, co zmienić, itp. jakiejś dodatkowej funkcjonalności raczej nie przewiduje, chyba, że bedzie to naprawdę ciekawy pomysł.

Porad chciałbym zasięgnąć od doświadczonych programistów smile.gif

A o to kod klasy:

  1. <?php
  2.  
  3. define('EXCEPTION_CRITICAL', 1);
  4. define('EXCEPTION_ERROR', 2);
  5. define('EXCEPTION_WARNING', 4);
  6. define('EXCEPTION_NOTICE', 8);
  7. define('EXCEPTION_INFO', 16);
  8. define('EXCEPTION_DEBUG', 32);
  9.  
  10. define("EXCEPTION_TARGET_DIR", "./logs");
  11. define("EXCEPTION_LOGGING_LEVEL", EXCEPTION_DEBUG);
  12. define("EXCEPTION_LENGTH_TYPE_STRING", 15);
  13.  
  14. class LoggedException extends Exception {
  15. protected $message="Unknown exception";
  16. protected $target_file="exc_main.txt";
  17. protected $target_path;
  18. protected $handled_file;
  19. protected $min_level;
  20. protected $level;
  21.  
  22. public function __construct($message=null, $level=EXCEPTION_ERROR, $code=null) {
  23. parent::__construct($message, $code);
  24. $this->target_path=(defined("EXCEPTION_TARGET_DIR")?EXCEPTION_TARGET_DIR:'.')."/";
  25. $this->min_level=defined("EXCEPTION_LOGGING_LEVEL")?EXCEPTION_LOGGING_LEVEL:EXCEPTION_ERROR;
  26. $this->level=$level;
  27. if(($file_handle=$this->file_handlers($this->target_file)) === false) {
  28. $this->handled_file=fopen($this->target_path.$this->target_file, 'a');
  29. $this->file_handlers($this->target_file, $this->handled_file);
  30. } else {
  31. $this->handled_file=$file_handle;
  32. }
  33. if(is_writeable($this->target_path.$this->target_file) && is_resource($this->handled_file)) {
  34. $this->WriteMsg("Exception '".get_class($this)."' with message '$this->message (code: $this->code)' in $this->file($this->line)", $this->level);
  35. if(EXCEPTION_DEBUG <= $this->min_level) {
  36. $backtrace=$this->getTrace();
  37. $this->WriteMsg("Stack trace (".(sizeof($backtrace)+1)."):", EXCEPTION_DEBUG);
  38. for($i=0; $i < sizeof($backtrace); $i++) {
  39. $args_str="";
  40. foreach($backtrace[$i]['args'] AS $var) {
  41. $args_str.=$this->getTypeAsString($var).", ";
  42. }
  43. $args_str=substr($args_str, 0, -2);
  44. $this->WriteMsg("#$i ".$backtrace[$i]['file']."(".$backtrace[$i]['line']."): ".$backtrace[$i]['class'].$backtrace[$i]['type'].$backtrace[$i]['function']."($args_str)", EXCEPTION_DEBUG);
  45. }
  46. $this->WriteMsg("#$i {main}", EXCEPTION_DEBUG);
  47. }
  48. }
  49.  
  50. }
  51. final protected function WriteMsg($msg, $level=EXCEPTION_INFO) {
  52. if(is_resource($this->handled_file)) {
  53. if($level <= $this->min_level) {
  54. $time=strftime('%d.%m.%Y %H:%M:%S', time());
  55. $str_level=$this->LevelToString($level);
  56. $msg=str_replace("t", " ", str_replace(array("rn", "n", "r"), " ", $msg));
  57. fwrite($this->handled_file, "$timet$str_levelt$msgn");
  58. }
  59. }
  60. }
  61. final protected function file_handlers($file, $handle=null) {
  62. static $handlers;
  63. if(!isset($handlers)) $handlers=array();
  64. if(is_null($handle)) {
  65. if(isset($handlers[$file])) {
  66. return $handlers[$file];
  67. } else {
  68. return false;
  69. }
  70. } else {
  71. if(isset($handlers[$file])) {
  72. return false;
  73. } else {
  74. $handlers[$file]=$handle;
  75. return true;
  76. }
  77. }
  78. }
  79. final public function getLevel() {
  80. return $this->level;
  81. }
  82. final public function getLevelAsString() {
  83. return $this->LevelToString($this->level);
  84. }
  85. final protected function LevelToString($level) {
  86. $list=array(
  87. EXCEPTION_CRITICAL => "EXCEPTION_CRITICAL",
  88. EXCEPTION_ERROR => "EXCEPTION_ERROR",
  89. EXCEPTION_WARNING => "EXCEPTION_WARNING",
  90. EXCEPTION_NOTICE => "EXCEPTION_NOTICE",
  91. EXCEPTION_INFO => "EXCEPTION_INFO",
  92. EXCEPTION_DEBUG => "EXCEPTION_DEBUG"
  93. );
  94. return isset($list[$level])?$list[$level]:"[unknown]";
  95. }
  96. final protected function getTypeAsString($var) {
  97. if(is_bool($var)) {
  98. return "bool(".($var===true?"true":"false").")";
  99. } else if(is_integer($var)) {
  100. return "int($var)";
  101. } else if(is_float($var)) {
  102. return "float($var)";
  103. } else if(is_double($var)) {
  104. return "double($var)";
  105. } else if(is_real($var)) {
  106. return "real($var)";
  107. } else if(is_null($var)) {
  108. return "NULL";
  109. } else if(is_string($var)) {
  110. $val=(defined("EXCEPTION_LENGTH_TYPE_STRING") && strlen($var) > EXCEPTION_LENGTH_TYPE_STRING)?substr($var, 0, EXCEPTION_LENGTH_TYPE_STRING)."[...]":$var;
  111. return "string(".strlen($var).") '".str_replace("t", "t", str_replace(array("rn", "n", "r"), "n", $val))."'";
  112. } else if(is_resource($var)) {
  113. return "resource '".substr($val=strval($var), strrpos($val, "#")+1)."' of type '".get_resource_type($var)."'";
  114. } else if(is_array($var)) {
  115. $array_str="{ ";
  116. foreach($var AS $key=>$val) {
  117. $array_str.="[$key] => ".$this->getTypeAsString($val).", ";
  118. }
  119. $array_str=substr($array_str, 0, -2)." }";
  120. return "array(".sizeof($var).") $array_str";
  121. } else if(is_object($var)) {
  122. return "object of class '".get_class($var)."'";
  123. } else {
  124. return "unknown type '$var'";
  125. }
  126. }
  127. final public function __destruct() {
  128. if(is_resource($this->handled_file)) {
  129. fclose($this->handled_file);
  130. }
  131. }
  132. }
  133.  
  134. ?>


Pozdrawiam
pawel_k
do oceniania jest osobne forum...
a co do klasy wygląda ok smile.gif tylko metoda getTypeAsString z tymi wszystkimi if'ami wygląda dziwnie, popatrz na funkcję http://pl.php.net/manual/en/function.gettype.php
likemandrake
Witam

Znam wlasnie ta funkcje, tylko no niestety, nie zaspokoila moich potrzeb.
Jak widac, wraz z typem danych zwracam rowniez jakies informacje, np rozmiar tekstu, zawartosc tablicy. Chcialbym zachowac funkcjonalnosc tej metody taka jaka jest, a nie wiem, jak da sie to zrobic w inny sposob.

Pozdrawiam, no i komentarze nadal mile widziane
starach
A zastosowanie gettype w czym ci przeszkadza ?
U mnie to wygląda tak.
  1. <?php
  2. public function parseArgs($args)
  3. {
  4. $arg = '';
  5. foreach($args as $val) 
  6. {
  7. $type = gettype($val);
  8. if($type == 'boolean') {
  9. if($val) {
  10. $arg .= 'true, ';
  11. } else {
  12. $arg .= 'false, ';
  13. }
  14. } else if($type == 'string') {
  15. $arg .= "'$val', ";
  16. } else if($type == 'array') {
  17. $arg .= 'array('.count($val).'), ';
  18. } else if($type == 'object') {
  19. $arg .= 'object('.get_class($val).'), ';
  20. } else if($type == 'resource') {
  21. $arg .= 'resource('.get_resource_type($val).'), ';
  22. } else {
  23. $arg .= $val.', ';
  24. }
  25. }
  26. return substr($arg,0,strlen($arg)-2);
  27. }
  28. ?>
metoda getBacktrace() korzysta z tej metody sprawdzając typ zmiennej.
Poza tym twój kod jest mało czytelny zobacz czy nie da się tego rozbić na kilka innych metod.
I jeszcze jedna uwaga może to jest lekka paranoja,
ale konstrukcja alternatywna if ( warunek ? spełniony : niespełniony ) oprócz tego że gorzej się ją czyta wykonuje się dłużej od zwykłej.
( czy ktoś może dodać stripslashes to dej cholernej funkcji wyświetlającej kod PHP na forum bo zaczyna być to irytujące ? )
UDAT
Po pierwsze - po co tworzysz stałe globalne?? Nie prościej tworzyć stałe w klasie?
Po drugie zamieniłbym stałą Exception_Logging_Level na właściwość statyczną klasy.
Po trzecie - na mój gust metoda __construct jest przeładowana - dokonaj refaktoryzacji
Po czwarte - czemu wszystkie metody są final
Po piąte - jakoś nie odpowiada mi trzymanie logowania wyjątków w klasie wyjątków - jest to bliskie tworzenia boskich klas
Po szóste - zamiast tego dużego if'a użyłbyś var_export" title="Zobacz w manualu PHP" target="_manual
Ludvik
Cytat
Po piąte - jakoś nie odpowiada mi trzymanie logowania wyjątków w klasie wyjątków - jest to bliskie tworzenia boskich klas

Dziwię się, że tak późno to padło. Obiekt wyjątku powinien pełnić swoją rolę, czyli służyć do zbierania informacji o sytuacji, w której został wyrzucony. Logowanie to zadanie dla zupełnie innego obiektu. Lepiej nic nie mieszaj z wyjątkami, klasa Exception jest na tyle dobra jako baza, że tworzenie nowych typów wyjątków ma za zadanie nadać im odpowiednią nazwę. Nic więcej...

Pomijając fakt, że w ten sposób jesteś na sztywno związany z jednym sposobem logowania wyjątków. Nie zmienisz implementacji, bo:
  • musisz zmienić kod w klasie wyjątku
  • albo musisz zmienić nazwę wyjątku we wszystkich miejscach, w których wystąpiła
Utworzenie obiektu, który będzie logował wyjątki, jest dużo lepszym rozwiązaniem.
likemandrake
Witam

Dzieki chlopaki, za ostra jadke winksmiley.jpg

@UDAT

Co do definiowania zmiennych globalnych, od linii 3-8 niezmienie tego raczej, poniewaz, jesli tworze nowy wyjatek, to zdecydowanie bardziej przejrzyste jest uzycie takiego kodu:

Kod
throw new LoggedException("komunikat", EXCEPTION_WARNING, 26);


niz takiego

Kod
throw new LoggedException("komunikat", LoggedException::EXCEPTION_WARNING, 26);


jesli chodzi o nastepne 3 linijki definicji, to mam taki zcentralizowany plik konfiguracyjny (parsowanie pliku .ini) i objekt ktory to zczytuje, nastepnie definiuje wlasnie takie zmienne

Co do rozdzielenia klasy na klase do wyjatkow i do logowania myslalem nad tym, mam nawet gotowy logger, nie wiem, jakos mi to tak przyszlo, zeby zebrac to do jednej kupy, gdyz przychodzily mi do glowy rzeczy typu, a co jesli logger sie nie zaladuje, a to to, a to tamto, ale jednak po cos jest to OOP.

No nic, posiedze jeszcze nad tym i wielkie dzieki za porady smile.gif
UDAT
Jeszcze parę słów na koniec:
Ostatnio z coraz większym zainteresowaniem poznaję możliwości użycia AOP do poprawy jakości, które także w tym wypadku byłoby najlepszym rozwiązaniem. Oto szybko naskrobany aspekt:

  1. <?php
  2. class MyLogger {
  3. public static function Log ( $message, Exception $e );
  4. echo $message, PHP_EOL;
  5. var_dump ( $e );
  6. echo PHP_EOL;
  7. }
  8. }
  9. aspect LoggedException{
  10.  
  11.  public $instances = array(); //Trzyma unikalne instancje
  12.  
  13.  pointcut exc:new(Exception+(*));
  14.  
  15.  
  16.  around(): exc{
  17. $exceptionObject = proceed();
  18. MyLogger::Log ( "Catched exception ", $exceptionObject );
  19. return $exceptionObject;
  20.  }
  21. }
  22. ?>


Powyższy przykład zawiera wyekstrahowany z klasy wyjątku - kod odpowiedzialny za logowanie. Oczywiście klasę MyLogger musisz utworzyć odpowiednią do zastosowania.

Natomiast zamiast odpowiednich poziomów(podawanych w konstruktorze) wyjątków, które są niezbyt praktyczne, używać dziedziczenia, np.:
  1. <?php
  2. class InfoException extends Exception{}
  3. class ErrorException extends Exception{}
  4. class FatalException extends Exception{}
  5. ?>



Przykład napisany do użycia z phpAspect - opis instalacji jest na stronie projektu i na moim blogu ( patrz Automatyczny Singleton ). Zapraszam do poznawania tego podejścia do programowania.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.