Cuando queremos saber que está ocurriendo con nuestra aplicación o queremos depurar ciertas partes de nuestro código, normalmente acudimos a las clases Trace y Debug. Estas clases disponen de llamadas a funciones WriteXXX que nos permiten escribir en distintas salidas la información que creamos oportuna dependiendo de los TraceListeners que tengamos configurados.
.NET Framework 2.0 introduce la clase TraceSource como mejora al sistema de trazas del Framework 1.1.
Quizás la diferencia más clara con los conocidos Trace y Debug es que no es una clase estática, sino que podemos definir tantas instancias como consideremos oportuno.
Pero, ¿por qué instanciable? ¿No es más cómodo llamar directamente a Trace en vez de estar controlando la instancia de TraceSource por todo el código de la aplicación? Es posible que en un principio pueda parecer que sí, pero:
El funcionamiento de trazas sobre TraceSource está basado en la siguiente premisa: Los mensajes de traza son enviados a través de los switches hacia los listeners, los cuales escriben la información de la traza en el medio correspondiente.
El siguiente esquema muestra el flujo de información desde la llamada Trace* hasta que se escribe en la salida.

Vamos a ir viendo poco a poco este proceso con algunos ejemplos.
Vamos a ver un ejemplo muy sencillo de utilización de TraceSource:
Module Module1
Private mySource As New System.Diagnostics.TraceSource("PruebaTraceSource")
Sub Main()
mySource.TraceInformation(“Hola mundo!!!”)
mySource.TraceEvent(TraceEventType.Error, 1, "Mensaje de Error.")
mySource.TraceEvent(TraceEventType.Warning, 2, "Mensaje de precaución.")
mySource.TraceData(TraceEventType.Transfer, 3, New Object(){1, 2, 3, 5, 8, 13})
mySource.TraceTransfer(4, “Transferencia de datos.”, New Guid(“xxxx”))
mySource.Close()
Console.Write("Pulsa Enter para terminar.")
Console.ReadLine()
End Sub
End Module
Vemos que TraceSource es capaz de manejar distintos tipos de llamadas de Traza:
Si se compila y se prueba este código, comprobamos que no vemos ninguna traza. Esto es porque por defecto el Listener que hay envía el contenido a OutputDebugString, y a no ser que tengamos algo que lea los mensajes de allí, no veremos nada.
Para poder obtener una salida, es necesario crear y configurar los TraceListeners. Aunque se puedan crear mediante código, es aconsejable hacerlo a partir del fichero de configuración de la aplicación. Añadimos un fichero de configuración e introducimos el siguiente contenido:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.diagnostics>
<sources>
<source name="PruebaTraceSource"
switchName=”mySwitch”
switchType=”System.Diagnostics.SourceSwitch”
>
<listeners>
<add name="console" type="System.Diagnostics.ConsoleTraceListener"
/>
<remove name="Default"/>
</listeners>
</source>
</sources>
<switches>
<add name=”mySwitch” value=”All” />
</switches>
</system.diagnostics>
</configuration>
Si ejecutas ahora la aplicación, verás que la salida de la traza se muestra en la consola. Esto ocurre porque cuando creas la instancia de TraceSource en mySource, automáticamente la aplicación lee el fichero de configuración y comprueba que hay un TraceSource con el mismo nombre que la instancia: “PruebaTraceSource” y le asigna la configuración que hay en el fichero.
La salida que obtienes es:
PruebaTraceSource Information: 0 : Hola mundo!!!
PruebaTraceSource Error: 1 : Mensaje de Error.
PruebaTraceSource Warning: 2 : Mensaje de precaución.
PruebaTraceSource Transfer: 3 : 1, 2, 3, 5, 8, 13
PruebaTraceSource Transfer: 4 : Transferencia de datos., relatedActivityId=XXXX
Pulsa Enter para terminar.
Si nos fijamos en el fichero de configuración, vemos que hemos definido un switch para el TraceSource. Los switches permiten activar, desactivar o filtrar las trazas que van a ser procesadas por los listeners del TraceSource.
Hay 3 tipos de Switches:
Habitualmente el fichero de configuración mantendrá los switches apagados. Sólo en caso de problemas que es cuando se necesita la traza, es cuando activamos las trazas. Así evitamos que el PC se inunde con ficheros de trazas inservibles.
Vamos a ver el mismo ejemplo que antes, pero filtrando únicamente los mensajes Critical, Error y Warning. Esto lo hacemos únicamente cambiando el valor de “mySwitch” dentro del fichero de configuración:
<switches>
<add name="mySwitch" value="Warning" />
</switches>
Obtenemos la siguiente salida:
PruebaTraceSource Error: 1 : Mensaje de Error.
PruebaTraceSource Warning: 2 : Mensaje de precaución.
Pulsa Enter para terminar.
Para añadir nuevos listeners que reciban nuestros mensajes, basta con añadirlos a la colección de Listeners del TraceSource, bien mediante el fichero de configuración o bien indicándolo en el código fuente.
Estos son los tipos de TraceListeners que tenemos disponibles por defecto:
Es el listener por defecto. Los mensajes enviados a través de este listener son dirigidos a OutputDebugString y a Debugger.Log. Por lo tanto, a no ser que tengamos algún método que investigue estos objetos, no podremos ver la traza.
Redirecciona los mensajes a un objeto TextWriter o a cualquier instancia de Stream. Y dado que internamente es un Stream, esta clase implementa los métodos de Flush y Close, a los cuales hay que llamar antes de terminar la aplicación. Estos métodos también están disponibles desde el objeto TraceSource, lo que implica que va a hacer una llamada a estos métodos para cada uno de los listeners que tenga asociado en su colección.
Ejemplo:
<listeners>
<add name="myListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData = “TextWriterOutput.log” />
<remove name="Default"/>
</listeners>
Direcciona los mensajes de traza a un EventLog.
Ejemplo:
<listeners>
<add name="myEventLog"
type="System.Diagnostics.EventLogTraceListener" />
<remove name="Default"/>
</listeners>
Direcciona los mensajes de traza a la consola, tanto por la salida estándar como por la salida de error. Pero atención, para que la traza sea mostrada, la aplicación debe tener disponible la Consola. Así que cuidado con las aplicaciones no basadas en consola.
Al igual que TextWriterTraceListener redirecciona los mensajes de la traza a TextWriter o a cualquier Stream de escritura. La ventaja de esta clase es que permite definir un carácter como separador de campos, de manera que podamos a posteriori procesar la información de la traza de manera más sencilla. Para hacernos una idea, si definimos el carácter separador como una coma, entonces nuestra salida sería un fichero con el mismo formato que CSV, por lo que incluso seríamos capaces de abrirlo desde Microsoft Excel o importarlo a una base de datos.
Ejemplo:
<listeners>
<add name="myCsvListener"
type="System.Diagnostics.DelimitedListTraceListener"
delimiter=”,”
initializeData=”CSV_trace.csv” />
<remove name="Default"/>
</listeners>
Al igual que la DelimitedListTraceListener esta clase también es nueva en el Framework 2.0 y también utiliza un TextWriter o un Stream para escribir la traza. En este caso, como el nombre de la clase indica, se genera un fichero XML. Este fichero crea los siguientes nodos. Uno por cada mensaje de traza:
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
<System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
<EventID>0</EventID>
<Type>3</Type>
<SubType Name="Information">0</SubType>
<Level>8</Level>
<TimeCreated SystemTime="2007-02-13T21:05:35.3750000Z" />
<Source Name="TestApp" />
<Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
<Execution ProcessName="Articulo TraceSource.vshost" ProcessID="1000" ThreadID="10" />
<Channel />
<Computer>FIZBAN</Computer>
</System>
<ApplicationData>Hola mundo!!!</ApplicationData>
</E2ETraceEvent>
Quizá el problema es que esta clase no genera ningún nodo raíz, por lo que si queremos abrirlo con el Explorer o el XML Notepad nos dará un error de formato. Basta con modificar el fichero con cualquier editor de textos y ponerle manualmente un nodo raíz.
La clase TraceSource nos permite añadir opcionalmente a los mensajes de traza más información de manera sencilla. Esto se puede hacer a nivel de cada listener:
<listeners>
<add name="console"
type="System.Diagnostics.ConsoleTraceListener"
traceOutputOptions=”DateTime, Timestamp” />
</listeners>
Las opciones de las que disponemos son:
En el ejemplo hemos indicado que nos incluya la fecha y hora y el valor de time stamp. El resultado de la consola es:
PruebaTraceSource Error: 1 : Mensaje de Error.
DateTime=2007-02-13T22:10:40.5344751Z
Timestamp=272789157998744
PruebaTraceSource Warning: 2 : Mensaje de precaución.
DateTime=2007-02-13T22:10:40.5462338Z
Timestamp=272785317962347
Pulsa Enter para terminar.
Hemos visto también como activar o desactivar las trazas a partir de modificar el valor de los switches. El inconveniente que tienen los switches es que actúan sobre el TraceSource, y por ende, sobre todos los listeners que tiene asociados. Si lo que queremos es hacer un filtrado a nivel de listener, usaremos los objetos EventTypeFilter.
Vamos a ver un ejemplo donde definimos un filtro para la consola de manera que sólo se escriban los mensajes de error, dejando para el resto de los listeners todos los mensajes:
<configuration>
<system.diagnostics>
<sources>
<source name="TraceSourceApp"
switchName="mySwitch"
switchType="System.Diagnostics.SourceSwitch" >
<listeners>
<add name="console"
type="System.Diagnostics.ConsoleTraceListener"
>
<filter type="System.Diagnostics.EventTypeFilter" initializeData="Error" />
</add>
<add name="myListener"
type="System.Diagnostics.TextWriterTraceListener"
initializeData = “TextWriterOutput.log”
/>
<remove name="Default"/>
</listeners>
</source>
</sources>
<switches>
<add name="sourceSwitch" value="Verbose"/>
</switches>
</system.diagnostics>
</configuration> Este sistema de trazas tambien nos permite obtener información sobre el tráfico de red que nuestras aplicaciones generen. Para ello, tenemos que configurar los Sources en el fichero de configuración de la siguiente manera:
<configuration>
<system.diagnostics>
<sources>
<source name="System.Net">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Sockets">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="31" maxdatasize="2048"/>
<add name="System.Net.Sockets" value="Error"
/>
<add name="System.Net.Cache" value="Verbose"
/>
</switches>
<sharedListeners>
<add name="System.Net" type="System.Diagnostics.TextWriterTraceListener"
initializeData="Network.log" />
</sharedListeners>
</system.diagnostics>
</configuration>
Esta configuración te permite obtener la salida de las clases que pertenecen a System.Net. Esto se ve por los tipos de Switches que hemos definido:
| System.Net.Sockets | Envía la salida de algunos métodos públicos de las clases Socket, TcpListener, TcpClient y Dns. |
| System.Net | Envía la salida de métodos públicos de las clases HttpWebRequest, HttpWebResponse, FtpWebRequest y FtpWebResponse, e información de depuración sobre SSL. |
| System.Net.HttpListener | Envía la salida de métodos públicos de las clases HttpListener, HttpListenerRequest, HttpListenerResponse. |
| System.Net.Cache | Envía la salida de algunos métodos privados e internos de System.Net.Cache. |
Los switches se pueden configurar con los siguientes atributos:
| value | Atributo requerido que indica hasta que punto queremos generar traza. Los valores permitidos son: Critical, Error, Verbose, Warning, Information. |
| maxdatasize | Atributo opcional que indica el máximo valor de bytes que se van a incluir en cada línea de traza. El valor por defecto es 1024. |
| tracemode | Atributo opcional que indica los formatos en los que vamos a poder ver la traza. Los valores permitidos son protocolonly para ver el texto sólo (valor por defecto), e includehex para ver la traza en hexadecimal junto con la de texto. |
La traza que se genera tiene un aspecto similar a:
[18] (4357) Entering Socket#33874638::Send()
[18] (4387) Exiting Socket#33874638::Send()-> 61#61
En esta traza, el valor [18] corresponde al identificador del thread. Los números siguientes (4357) y (4387) corresponden al timestamp o tiempo transcurrido en milisegundos desde que comenzó la aplicación. El texto que viene a continuación viene a decirnos que un objecto con identificador 33874638 entró en una llamada a Send y salió de esta devolviendo el valor 61.
Este otro ejemplo muestra una traza sobre una comunicación en Http con el valor includehex activado:
[1692] (1142) 00000000 : 47 45 54 20 2F 77 70 61-64 2E 64 61 74 20 48 54 : GET /wpad.dat HT
[1692] (1142) 00000010 : 54 50 2F 31 2E 31 0D 0A-48 6F 73 74 3A 20 69 74 : TP/1.1..Host: it
[1692] (1142) 00000020 : 67 70 72 6F 78 79 0D 0A-43 6F 6E 6E 65 63 74 69 : gproxy..Connecti
[1692] (1142) 00000030 : 6F 6E 3A 20 43 6C 6F 73-65 0D 0A 0D 0A : on: Close....
Los siguientes enlaces han servido de documentación para este artículo:
http://msdn2.microsoft.com/en-us/library/system.diagnostics.tracesource.aspx