Muchas veces, los proyectos en PHP crecen y crecen, y pueden ocurrir múltiples errores, tanto a la hora de crearlos como cuando ya están entregados al cliente final y debemos ofrecer soporte.
Por eso es importante ser rápido localizando los errores, y, siempre que la página, o el programa no haga algo como debe, debería tomar nota de qué ha pasado, cómo ha sido y de los datos involucrados con el fin de poder solventar el problema. Tengo que decir que debemos ser inteligentes con estos criterios, ya que un usuario malintencionado puede hacernos perder todo el espacio que tengamos disponible en nuestro servidor provocando errores; por ejemplo un script que funcione en Ajax y no se le hayan entregado los parámetros necesarios, puede ser útil saberlo en tiempo de desarrollo, pero no cuando el proyecto esté funcionando en la web, puede que incluso un motor de búsqueda mal entrenado se dedique a entrar en ese script y tumbarnos el programa.
Para hacer un informe, lo más fácil es hacerlo en un fichero de texto (podemos crear un XML sin mucho esfuerzo más, aunque va a ser algo que sólo vamos a leer nosotros y como mucho los administradores de la página, en muchos casos a los administradores no les dejaremos verlo).
Para esto, yo dispongo de una clase con funciones para guardar un informe de errores:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | <?php // Activa la depuración define('__debug', true); // Activa la visualización de los errores (sólo para modo desarrollo) define('__debug_v', true); class my_logger { private $base_path; function __construct() { /** Sólo actuaremos si __debug es verdadero */ if (__debug) { define('__debug_file', 'my_log.log'); define("__gf_dateformat_log", "d/m/Y H:i"); // Si ejecutamos el programa desde consola. El directorio actual es PWD if (isset($_SERVER['DOCUMENT_ROOT'])) $base_path=$_SERVER['DOCUMENT_ROOT']; elseif (isset($_SERVER['PWD'])) $base_path=$_SERVER['PWD']; else $base_path=''; } } /** ****************************************************************** * @brief Escribir mensaje de error en pantalla * * To-do: Posibilidad de crear plantillas para este error desde el archivo de configuración * * @param $emsj Mensaje de error * * @return Nada * ******************************************************************/ private function displayerror($emsj) { return '<br /><span style="color: yellow;"'.wordwrap('DEP: '.$h->T($msj),70,"\n").'</span><br />'; } /** ****************************************************************** * @brief Extrae el nombre de una función o un método de un array * de backtrace * * @param $bt_data Array de backtrace * @param $index Índice del array * * @return Array[3]={linea, Función o método (clase::metodo), fichero} * ******************************************************************/ private function backtrace_function (&$bt_data, $index) { $bt_single=(isset($bt_data[$index]))?$bt_data[$index]:false; if (!$bt_single) /* No data, no function */ return false; $function=(isset($bt_single['function']))?$bt_single['function']:false; $class=(isset($bt_single['class']))?$bt_single['class']:false; $funcname=($class)?($class.'::'.$function):$function; $file=str_replace('path', '', $bt_single['file']); return array($bt_single['line'], $funcname, $file, $file.' ('.$funcname.', '.$bt_single['line'].')'); } /** ****************************************************************** * @brief Genera un fragmento de un error complejo. A menudo, esto * incluye información del backtrace u otras cosas. * Este método debe ser llamado desde preg_replace_callback() * * @param $data Datos enviados por preg_replace_callback() * * @return En qué se tiene que convertir la cadena enviada. * ******************************************************************/ private function complexError($data) { if (!isset($data[1])) return $this->writelog('%%where%%: No se ha llamado desde preg_replace_callback(); Origen: "%%origen%%"'); $btrace=debug_backtrace(); switch ($data[1]) { case 'where': $wdata=$this->backtrace_function ($btrace, 4); /* Nivel 0: complexError; Nivel 1:preg_replace_callback(); Nivel 2: writelog; Nivel 3: wlog(); Nivel 4: El que queremos */ return $wdata[3]; break; case 'origen': $wdata=$this->backtrace_function ($btrace, 5); /* Nivel 3: dónde llamamos a la función */ return $wdata[3]; break; case '-origen': $wdata=$this->backtrace_function ($btrace, 6); /* Nivel 3: dónde llamamos a la función */ return $wdata[3]; break; case '--origen': $wdata=$this->backtrace_function ($btrace, 7); /* Nivel 3: dónde llamamos a la función */ return $wdata[3]; break; default: return $this->writelog('%%where%%: No se encuentra la palabra clave: "'.$data[1].'"'); break; } } /** ****************************************************************** * @brief Escribimos un mensaje en el log. * * @param $error Error a escribir * @param $display Mostrar error en pantalla (sólo si __debug_v vale true) * * @return false para usarlo como return de alguna función * ******************************************************************/ function writelog($error, $display=false) { if ((__debug) && ($error)) { $errorw=preg_replace_callback('/%%(.*?)%%/', array($this, 'complexError'), $error); $log_f = fopen(__debug_file, "a+"); fputs($log_f, date(__gf_dateformat_log)." - ".$errorw."\n"); fclose($log_f); if ((__debug_v)&&($display)) $this->displayerror($errorw); } return false; } }; $log = new my_logger(); function wlog($error, $display=false) { $GLOBALS['log']->writelog($error, $display); } function genera_error() { wlog("Genero un error nada más empezar y digo dónde está %%where%% llamado por %%origen%%"); } function original() { genera_error(); } original(); ?> |
Cada vez que encontramos un error, podemos llamar a wlog() para guardar el informe del error que se guardará en un archivo de texto.
Mientras estamos desarrollando la aplicación podemos definir __debug_v a true para visualizar algunos errores (que previamente enviaremos con wlog(«error», true)).
Además, a la hora de enviar un error, disponemos de las palabras clave %%where%%, %%origen%%, %%-origen%% y %%–origen%% para identificar automáticamente en qué función se ha producido el error, desde qué línea se llama así como la función desde donde se llama a la que provoca el error, y la anterior…
Si utilizamos la opción para averiguar automáticamente dónde está el error (la alternativa sería escribir el nombre de la función en el texto del error) tardará un tiempo en ejecutarse, ya que estamos escrudiñando dónde se ha producido, estamos haciendo un backtrace. De hecho la función PHP que genera ese informe es debug_backtrace() y nos resultará de gran ayuda para la depuración de nuestras aplicaciones.
Pingback: Bitacoras.com /