Nel tentativo di semplificare il tracing delle applicazioni .NET, nel framework 2.0 è stata introdotta la classe TraceSource (in System.Diagnostics) la quale affianca l'ormai nota classe Trace.
Grazie a TraceSource effettuare il tracing di una applicazione vuol dire scrivere del codice tipo:
Trace
.AutoFlush=true;
TraceSource ts = new TraceSource("MySource");
TextWriterTraceListener tw = new TextWriterTraceListener("c:\\log.txt","log");
tw.TraceOutputOptions=TraceOptions.DateTime | TraceOptions.ProcessId;
ts.Listeners.Add(tw);
ts.Switch.Level = SourceLevels.Error | SourceLevels.ActivityTracing;
ts.TraceInformation("Trace demo...");
ts.TraceEvent(TraceEventType.Start, 0,"Logging started");
ts.TraceData(TraceEventType.Information,99,new Person());
ts.TraceEvent(TraceEventType.Error, 1, "Error 1");
ts.TraceEvent(TraceEventType.Information, 2, " Some infos..:");
ts.TraceEvent(TraceEventType.Stop, 3,"Logging ended");
Il codice, dopo avere creato un istanza di TraceSource identificata come "MySource" aggiunge alla collezione Listeners della stessa un TextWriterTraceListener il quale si occuperà di scrivere effettivamente le informazioni che vorremo loggare specificando delle TraceOutputOptions che verranno aggiunte automaticamente dal listener al nostro log (in questo caso vogliamo vengano loggate ina ggiunta anche Data e Ora e il ProcessId delle nostra applicazione)
Attraverso la proprietà Switch di TraceSource possiamo specificare il livello di tracing da considerare, nell'esempio ho usaro Errors e ActivityTracing (novità), quest'ultimo è utile quando desideriamo loggare informazioni relative al flusso della nostra applicazione (es.Start/Stop/Suspend...)
I vari metodi TraceEvent,TraceInformation e TraceData ci consentono di loggare le informazioni verso il/i listeners agganciati a TraceSource, in particolar modo il primo parametro di TraceEvent identifica il livello di tracing dell'informazione, il quale verrà eventualmente ignorato se non conforrme a quanto specificato in TraceSource.
E' chiaro che inserire direttamente i dettagli di tracing nel codice ha poco senso, sopratutto quando il codice sopra indicato si può ridurre a:
Trace.AutoFlush=true;
TraceSource ts = new TraceSource("MySource");
ts.TraceInformation("Trace demo...");
...
e aggiungendo i listeners e gli switch dinamicamente nel file .config in questo modo:
<
configuration>
<system.diagnostics>
<sources>
<source name="MySource" switchName="MySwitch">
<listeners>
<add initializeData="c:\output.txt" type="System.Diagnostics.TextWriterTraceListener" name="MyTxtSwitch"/>
<add name="xmlListener" />
</listeners>
</source>
<source name="MySource2" switchValue="Verbose">
<listeners>
<add name="xmlListener" />
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="C:\output.xml" type="System.Diagnostics.XmlWriterTraceListener"
name="xmlListener" traceOutputOptions="None" />
</sharedListeners>
<switches>
<add name="MySwitch" value="Error" />
</switches>
</system.diagnostics>
</configuration>
Per aggiungere le informazioni relative ad un determinato TraceSource dobbiamo innazitutto definire all'interno di <sources> una elemento <source> il cui attributo name coincida esattamente con quello del nostro TraceSource (es: MySource) dopodichè definire il livello di tracing che può avvenire secondo due modalità, usando switchName="MySwitch" e definendo una sezione <switches> all'interno della quale descrivere lo switch che può, in questo caso, essere condiviso da più TraceSources oppure usando switchValue="Verbose" andando ad agire quindi sul solo TraceSource indicato dall'attributo name.
Per i listeners il concetto è molto simile, se vogliamo configurare dei listeners che vogliamo vengano condivisi da più TraceSources possiamo farlo all'interno dell'elemento <sharedListeners> e referenziare tali listeners dai vari TraceSource per mezzo dell'attributo name.
Dal file noterete che ora ai vari listeners già presenti nell'attuale versione del framework si è aggiunto un XmlWriterListener e un DelimitedListTraceListener che, by default,salva le info in formato comma separated (il delimitatore è configurabile).