Guardar un log de los errores de nuestra aplicación
Fuente: a nil value.
2 del 3 de 2010
Lo primero que hay que tener en cuenta es que la mayoría de los errores que ocurren en nuestra página web deben estar controlados, pero puede suceder que ocurran excepciones en lugares que no habiamos previsto. Incluso aunque sepamos que tenemos todo controlado creo que siempre es recomendable establecer un proceso para los errores globales de nuestra aplicación, aunque nunca se utilice.
Para ello implementaremos dos versiones de la misma función, a la que llamaremos, por ejemplo, "lanzarError"; la primera recibirá la excepción que hemos controlado desde nuestra página web, la tratará y la almacenará en una ruta específica.
public static int lanzarError(Exception ex)
{
StringBuilder errorInfo = new StringBuilder();
//obtenemos la traza del error
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
//creamos un contenido que incluiremos en el log del error
errorInfo.Append("Método: " + ex.TargetSite.ToString() + "n");
errorInfo.Append("Código: " + ex.Source + "n");
errorInfo.Append("Mensaje de Error: " + ex.Message + "n");
errorInfo.Append("Traza de la Pila: " + ex.StackTrace + "n");
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("Excepción:" + ex.ToString());
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("*******************************************************************" + "n");
//añadimos información adicional (Es necesario cargar el .pbd de la aplicación)
errorInfo.Append("Método:" + trace.GetFrame(0).GetMethod().Name + "n");
errorInfo.Append("Linea:" + trace.GetFrame(0).GetFileLineNumber().ToString() + "n");
errorInfo.Append("Columna:" + trace.GetFrame(0).GetFileColumnNumber().ToString() + "n");
//obtenemos el nombre del fichero añadiendo un sufijo
string nombreFichero = obtenerNombreFichero("CAUGH_EX");
//escribimos en el fichero del log
if (escribirFichero(errorInfo.ToString(), nombreFichero) == -1)
{
return -1;
}
else
{
return 1;
}
}
Cómo podemos ver no tiene ningún secreto, crearemos un StringBuilder con lo que queramos guardar en nuestro fichero de log, elegimos un nombre para el fichero y escribimos.
Esta función hace uso de dos funciones adicionales; una para establecer el nombre del fichero y otra para escribir en él.
protected static string obtenerNombreFichero(string sufijo)
{
string fecError = "";
string codError = "";
DateTime Now = System.DateTime.Now;
//DateTimeFormatInfo dfi = new DateTimeFormatInfo();
//establecemos el formato que tendrá la fecha incluida en el nombre
//dfi.FullDateTimePattern = "dd-MM-yyyy_HHmmss";
fecError = String.Format("{0:dd-MM-yyyy_hh.mm.ss}", Now);
codError = fecError + "_" + sufijo;
string nombreFichero = "Error_" + codError + ".txt";
return nombreFichero;
}
Podemos elegir el nombre del fichero que queramos. En este caso he pensado que establecer la fecha completa sería una buena idea, pero ojo!, tenemos que tener en cuenta que si no añadimos un nombre único podemos encontrarnos con ficheros duplicados. Tenemos dos opciones. O comprobamos que si existe un fichero con ese nombre añadimos un sufijo adicional, o añadimos más parámetros a la hora (¿milisegundos?).
protected static int escribirFichero(string texto, string nombreFichero)
{
//obtenemos la ruta donde crearemos el log. Podemos enviarla desde nuestra aplicación
string ruta = ConfigurationSettings.AppSettings.Get("errorPath") + "errores";
//controlamos que existe la ruta y si no la creamos
if (!System.IO.Directory.Exists(ruta))
{
System.IO.Directory.CreateDirectory(ruta);
}
//creamos el flujo de salida para escribir
System.IO.StreamWriter sw = new System.IO.StreamWriter(ruta + nombreFichero, false);
try
{
//intentamos escribir en el fichero
sw.Write(texto);
sw.Flush();
return 1;
}
catch (Exception ex)
{
return -1;
}
finally
{
sw.Close();
}
}
Esta segunda función nos servirá para escribir en el fichero, enviándole previamente una ruta en donde almacenar los ficheros del log. En este ejemplo la ruta se obtiene desde un fichero de configuración de la aplicación, pero si queremos encapsular totalmente el registro de errores deberemos enviar esta ruta desde nuestra aplicación web y habrá que añadirlo como parámetro en la cabecera de la función.
Finalmente tenemos la segunda versión de lanzarError que sobrecarga a la primera:
public static int lanzarError(HttpContext ctx)
{
//Deberemos pasar información de la sesión desde nuestra aplicación
//HttpContext ctx = HttpContext.Current;
//obtenemos le ultimo error ocurrido
Exception exception = ctx.Server.GetLastError();
StringBuilder errorInfo = new StringBuilder();
//obtenemos la traza del error
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(exception, true);
//creamos un contenido que incluiremos en el log del error
errorInfo.Append("Página del Error: " + ctx.Request.Url.ToString() + "n");
errorInfo.Append("Código: " + exception.Source + "n");
errorInfo.Append("Mensaje de Error: " + exception.Message + "n");
errorInfo.Append("Traza de la Pila: " + exception.StackTrace + "n");
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("Excepción:" + exception.ToString());
errorInfo.Append("*******************************************************************" + "n");
errorInfo.Append("*******************************************************************" + "n");
//añadimos información adicional (Es necesario cargar el .pbd de la aplicación)
errorInfo.Append("Método:" + trace.GetFrame(0).GetMethod().Name + "n");
errorInfo.Append("Linea:" + trace.GetFrame(0).GetFileLineNumber().ToString() + "n");
errorInfo.Append("Columna:" + trace.GetFrame(0).GetFileColumnNumber().ToString() + "n");
//obtenemos el nombre del fichero añadiendo un sufijo
string nombreFichero = obtenerNombreFichero("UNCAUGH_EX");
//escribimos en el fichero del log
if (escribirFichero(errorInfo.ToString(), nombreFichero) == -1)
{
return -1;
}
else
{
return 1;
}
}
Cómo podemos ver es similar a la primera salvo que en este caso se envía la información sobre el contexto de la aplicación y de ahí obtendremos el último error ocurrido. Esta función nos permitirá registrar excepciones no controladas, pero para ello primero deberemos añadir un par de cosas en el fichero "Global.asax" de nuestra aplicación web:
protected void Application_Error(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
exCatcher.lanzarError(ctx);
ctx.Response.Redirect("Error.aspx");
}
El evento Application_Error ya existe por defecto en el Global.asax, así que solo tendremos que añadir el contenido. Esto unicamente obtendra el estado de la aplicación y lo enviará a nuestra función lanzarError. Finalmente redirigiremos a una página de Error que nos habremos creado previamente y en la que informaremos al usuario de lo que creamos conveniente.
Y eso es todo. Esto podemos complicarlo todo lo que queramos para añadirle más posibilidades.
Espero que haya servido de ayuda.
Un saludo


