Les voy a presentar una herramienta que me cambió la forma de hacer logging en mis aplicaciones .NET. Estoy hablando de TraceTool, creado por Thierry Parent.
TraceTool consiste en una librería (disponible para .NET, Java, Delphi, Javascript, entre otros lenguajes) para hacer tracing o logging (como prefieran llamarlo) y un visor (de uso opcional) que es capaz de interpretar tanto los logs o trazas generadas por la librería como así también otros orígenes.
En este post me voy a limitar a hacer una presentación de la herramienta (actualmente en su versión 11), pero para más información pueden consultar el artículo original, el cual contiene muchos más detalles sobre el funcionamiento y configuración de la misma.
Uso de la librería
En la mayoría de los casos la única clase con la que vamos a necesitar interactuar directamente es TTrace. Esta clase expone 3 propiedades, Debug, Warning y Error, las cuales se utilizan para registrar logs de información, advertencia y errores respectivamente. Veamos un ejemplo:
1: public void Log() {
2: TTrace.Debug.Send("Esto es un mensaje.");
3: TTrace.Warning.Send("Esto es una advertencia...");
4: TTrace.Error.Send("Esto es un mensaje de error!");
5: }
Ese código produce la siguiente salida (vista en el visor de TraceTool):
Como se ve, lo único que diferencia a cada tipo de mensaje, es el icono que lo precede. Si lo deseamos, también podemos agregar un comentario o texto asociado al mensaje de la siguiente manera:
TTrace.Debug.Send("Esto es un mensaje.", "Texto con info extra sobre el mensaje...");
Que nos quedaría así en el visor:
Una característica por de más de interesante es el hecho de que el método Send() nos devuelve una instancia de un objeto de tipo TraceNode (que es la clase base de la cual derivan los objetos Debug, Warning y Error), la cual nos permite seguir enviando trazas pero anidadas dentro de la traza original (es decir, sub-trazas):
1: TraceNode nodo = TTrace.Debug.Send("Nodo nivel 0.");
2: nodo.Send("¡Me encuentro en el nivel 1!");
3: nodo.Send("¡Yo tambien!");
4: nodo.Send("¡Y yo!");
5: TTrace.Debug.Send("Otro nodo nivel 0.");
Un efecto parecido se puede lograr con los métodos Indent() y UnIndent() que nos permiten indentar todas las trazas sin necesidad de guardar una referencia a un nodo en particular. Esto suele ser muy útil cuando logueamos las entradas, procesamiento y salidas de ciertos métodos aunque para ello TraceTool ya tiene un set de métodos específicamente adaptado para tal fin:
1: private void UnMetodo() {
2: TTrace.Debug.EnterMethod("UnMetodo()");
3:
4: try {
5: TTrace.Debug.Send("Procesando...");
6: // ...
7: throw new Exception();
8: } catch {
9: // Aca se podría loguear la excepción
10: TTrace.Error.Send("Error!");
11: } finally {
12: TTrace.Debug.ExitMethod("UnMetodo()");
13: }
14: }
Esto produce la siguiente salida:
Loguear más que sólo texto
Con TraceTool también es posible loguear el contenido de objetos enteros con la misma facilidad con la que logueamos sólo texto:
1: public class Persona {
2: public string Nombre { get; set; }
3: public string Apellido { get; set; }
4: public int Edad { get; set; }
5: }
6:
7: /*...*/
8:
9: TTrace.Debug.Send("Logueando solo texto");
10: var p = new Persona() { Nombre = "Gerardo", Apellido = "Contijoch", Edad = 27 };
11: TTrace.Debug.SendObject("Logueando un objeto", p);
También es posible incluir en la información a loguear sobre un objeto detalles como sus campos, métodos, eventos, miembros privados, etc.
En el caso que quisiéramos registrar el valor de una variable de un tipo primitivo, como puede ser un número, este método no resulta práctico (¿para qué mostrar información de la clase Int32 cuando solo queremos su valor?) y es por eso que la clase TraceNode posee el método SendValue():
1: TTrace.Debug.SendValue("Logueando un Int32", 50);
2: TTrace.Debug.SendValue("Logueando un DateTime", new DateTime(2005, 10, 6));
3: TTrace.Debug.SendValue("Logueando un Boolean", false);
(Los logs pueden configurarse para que no se registre la información como la hora y el hilo en el que se ejecutó el logueo)
Como si todo esto fuera poco, también existen métodos similares para registrar Xmls (AddXml()), imágenes (SendBitmap()) y hasta tablas (SendTable()). Pueden ver su uso en el artículo donde se presenta TraceTool.
Configuración
Hay dos puntos donde se puede configurar el logueo. El primero es la propiedad Options de la clase TTrace. Esta propiedad nos va a permitir configurar el formato de los logs que se registren.
Para controlar la verbosidad de los logs se pueden usar las propiedades SendEvents, SendFunctions, SendInherited, SendPrivate, SendProcessName y SendThreadId, las cuales permiten configurar que información se registra sobre los objetos que logueamos.
La otra propiedad importante de esta clase es SendMode, la cual nos permite indicarle a la API si vamos a enviar logs al visor (util cuando se depura, pero no siempre en producción) o no.
El otro punto donde podemos configurar el logueo es la propiedad WinTrace de TTrace. TTrace internamente utiliza a la clase WinTrace para registrar todo y es por ello que algunos parámetros referidos al logueo se especifican acá.
Para setear el path a un archivo de logs vamos a usar el método SetLogFile(). Junto con el path de logs también se puede configurar el particionamiento de los logs, es decir, si se crea un log diferente por día, por máximo de líneas o simplemente un único log ilimitado.
Lamentablemente, los logs son únicamente en formato Xml y esto no se puede cambiar.
Cabe aclarar que TraceTool tiene 2 logs diferentes, uno es el local (el que normalmente usaremos nosotros) y otro es el log del Visor, el cual puede registrar todo lo que se le envíe (usar este log es útil cuando hay mas de una aplicación logueando al mismo lugar). Ambos logs pueden ser configurados independientemente el uno del otro. Para más detalles sobre la configuración pueden ver el artículo original en donde se detalla un poco más el asunto.
Soporte Unicode
Desafortunadamente, si bien la API tiene soporte para logs encodeados como Unicode, el visor aún no los soporta y no es posible abrir logs con caracteres especiales. Es por ello que vamos a tener que cuidarnos de lo que guardamos en los logs porque puede que no podamos abrirlos con el visor. Para solucionar esto, yo opté por modificar la API (el código fuente esta disponible para bajar) y filtrar todos los caracteres especiales con una versión ligeramente modificada del método que presenté en este post (ojo con las tablas, que necesitan los caracteres ‘\t’ para ser procesadas correctamente).
Bien, como dije al comienzo, esto fue sólo una breve presentación de la herramienta, no quise entrar en muchos detalles porque iba a terminar siendo una reescritura completa del artículo original de Thierry Parent. Espero que les resulte interesante esta herramienta y por sobretodo, útil.
¡Nos vemos en el próximo post!
Publicado originalmente en https://gerardocontijoch.wordpress.com.