Debugging en Tracing met Visual Basic .NET
Met de komst van een nieuwe ontwikkeltaal en haar -omgeving wordt er vaak het eerst gekeken naar welke uitbreidingen de taal biedt. Daarnaast is er veel aandacht voor zaken als verbeterde Intellisense, automatische codegeneratoren of het kunnen toepassen van OOP technieken. Vaak vergeet je de eventuele verbeteringen te bestuderen die de 'vernieuwde debugger' je biedt. Aangezien je de debugger dagelijks zult gebruiken, is dit eigenlijk heel vreemd. Door je te verdiepen in de (nieuwe) mogelijkheden hiervan, kun je op een snellere manier, betere en stabielere applicaties schrijven. En dat elke dag weer...
De debugging-mogelijkheden van Visual Studio .NET zijn sterk verbeterd in vergelijking tot die van Visual Studio 6. Aan de ene kant bestaan die verbeteringen in uitbreidingen binnen de IDE en aan de andere kant heb je nu ook objecten beschikbaar waarmee je je sourcecode of applicatie kunt instrumenteren.
Soorten fouten
Wanneer we praten over fouten opsporen en het oplossen ervan, is het verstandig om eerst eens te kijken welke typen fouten er kunnen optreden. Er zijn in feite drie typen fouten: Syntax errors, Run-Time errors en Logical errors.We praten over 'Syntax errors' wanneer de compiler de code niet kan compileren. Dit is het geval bij typefouten of bij een functie-aanroep zonder de vereiste parameters. Deze fouten zijn eenvoudig op te sporen. Telkens wanneer de code gecompileerd wordt, worden deze fouten in de code onderstreept. Tevens worden ze toegevoegd aan de 'Tasklist'. Wanneer je dubbelklikt op zo'n fout spring je direct naar de desbetreffende code. In veel gevallen is de foutomschrijving zo duidelijk, dat het oplossen van de fout eenvoudig is. 'Run-Time errors' treden op wanneer je applicatie iets probeert te doen, wat op dat moment niet mogelijk of toegestaan is. Hierbij kun je denken aan delen door nul, of door een bestand te openen op een niet beschikbare netwerkbron. Dit type fouten is op te vangen met foutafhandeling en deels te voorkomen door defensief te programmeren. Wat ingewikkeldere fouten kun je opsporen met de tools beschreven hieronder. De zogenaamde 'Logical errors' zijn de meest lastige fouten die kunnen optreden. Je krijgt geen foutmelding en alles lijkt goed te gaan. Het resultaat is echter niet wat je verwacht: berekeningen kloppen niet of onjuiste data is weggeschreven naar de database. Met name het tracen van informatie kan in dit soort gevallen uitkomst bieden, maar daarover later meer.
Visual Studio .NET 2003 IDE
De IDE van Visual Studio zit boordevol schermen en tools om je code te debuggen. Misschien zijn ze niet allemaal als 'nieuw' voor deze versie te betitelen, maar ze zijn zeker het vermelden waard.
Configuration Manager
De 'Configuration Manager' is een onderdeel die zeer in het oog springt. Deze optie bevindt zich namelijk, als combobox, zeer prominent in de standaard toolbar. Met deze tool kun je zelf aangeven of een assembly gecompileerd, en zo ja op welke wijze het gecompileerd moet worden. Belangrijk is om te weten, dat men code alleen kan debuggen wanneer het als zodanig, dus 'Debug' wordt gecompileerd. In dit geval wordt er een zogenaamd 'debugging symbol' bestand (*.pdb) aangemaakt. Dit bestand is essentieel voor het kunnen debuggen van je code. Het bevat informatie over functienamen of namen van controls en bevindt zich in de \bin-map. Het maakt het mogelijk om tijdens het debuggen leesbare broncode te kunnen weergeven.

Afbeelding 1. Configuration Manager
Het compileren in het algemeen, maar ook het aanmaken van zo'n pdb-bestand is een tijdrovende bezigheid. Met name in trajecten waarbij de solution uit meerdere projecten bestaat kun je hier veel kostbare tijd mee winnen door, voor onderdelen die al uitgebreid getest zijn, het opnieuw compileren uit te zetten. Buildconfiguraties bestaan op twee niveaus. Een daarvan is het solutionniveau, dat invloed heeft op een build van alle projecten binnen die solution. Het andere niveau is het project niveau, dat invloed heeft op het afzonderlijke project.
Breakpoints
Het instellen van breakpoints is waarschijnlijk wel de meest gebruikte manier van debuggen. De mogelijkheden van deze breakpoints zijn fors uitgebreid. Zo ben je flexibeler in het gebruik van condities, worden de 'breakpoint-instellingen' opgeslagen en kan je (bepaalde) breakpoints ook eenvoudig tijdelijk buiten gebruik stellen. Met name de laatste twee genoemde uitbreidingen vind ik erg prettig. Wanneer je de 'Breakpoints-window' selecteert heb je een duidelijk overzicht van alle breakpoints die gedefinieerd zijn en kun je deze vanuit hier ook makkelijk verwijderen of tijdelijk buiten gebruik zetten
Ouput-window
In de Output-window wordt tijdens het compileren en uitvoeren van het programma, alle command-line informatie getoond. Hierbij kun je denken aan welke assemblies geladen zijn, maar ook de informatie die, via Debug.Write, beschikbaar komt. Je kunt dit scherm zichtbaar maken via: View – Other Windows – Ouput.
Locals-, Autos- en Watch-windows
Een drietal windows die erg op elkaar lijken zijn de Locals-, Autos- en Watch-windows. In alle drie de windows kun je tijdens het debuggen de status en waarden zien van variabelen. Alle drie schermen bestaan uit een drietal kolommen: de naam, de waarde en het type van de variabele. Complexe structuren of objectcollecties worden weergeven als een treeview, zodat de bijbehorende datastructuren eenvoudig opengeklapt kunnen worden.
De Locals-window toont de waarden van alle variabelen binnen de huidige procedure. Dit in tegenstelling tot de Autos-window. Dit window toont binnen C# de variabelen van de huidige en vorige regel en voor Visual Basic .NET de huidige coderegel en drie regels ervoor en erachter. De Watch-window heeft tot doel de waarde van bepaalde variabelen het hele programmaverloop te volgen; ook al raken ze 'out-of-scope'. Je hebt een viertal Watchwindows tot je beschikking, zodat je je watches ook nog kan structureren en groeperen. De inhoud van al deze schermen wordt tijdens het afsluiten van je IDE opgeslagen bij het project, zodat je deze een volgende keer weer direct tot je beschikking hebt.
Je kunt de waarden van de numerieke- en stringwaarden ook vanuit deze schermen aanpassen. Klik op de waarde (Value) en type een andere waarde. De tekst wordt rood en het programma gaat vervolgens met deze nieuwe waarden verder. Helaas is dit, hoewel natuurlijk wel begrijpelijk, niet mogelijk voor complexe class- of structure variabelen.
Command- en Immediate-window
Het Command-window is een window wat door een groot aantal programmeurs niet helemaal begrepen wordt. Vanuit dit scherm kun je opdrachten uitvoeren. Hierbij kun je denken aan opdrachten die je anders via het menu kunt selecteren. Ondermeer door Ctrl+Alt+A kun je dit scherm tonen en door bijvoorbeeld:
> nav msdn.microsoft.nl
... te typen, open je een website binnen de Visual Studio .NET IDE. Het is verbazingwekkend hoeveel opdrachten beschikbaar zijn. Gelukkig is er in dit scherm Intellisense aanwezig, zodat je zonder veel zoekwerk alle mogelijkheden kunt uitproberen. Het Command-window kun je ook draaien in de zogenaamde 'Immediate-mode'. Vanuit de Immediate-window kun je tijdens het debuggen eigen methodes aanroepen, niet-complexe variabelen aanpassen of waarden direct naar het Command-window schrijven met behulp van het vraagteken. Voorbeeld:
? 10*7
? btnTest.Text.
Andere windows
Naast bovenstaande windows, heb je nog de beschikking over een aantal andere tools die je behulpzaam kunnen zijn bij het debuggen. Zo is er binnen Visual Basic .NET de Me-window (binnen C# de This-window) waar je een overzicht krijgt van alle members van het object wat geassocieerd is met de huidige method. Meestal het Formobject dus. In de Call Stack-window zie de procedurestack, dus welke weg de code heeft bewandeld om uiteindelijk in de huidige functie te belanden. In afbeelding 2 kun je zien dat er een break staat in de functie MijnProcedureC en dat deze is aangeroepen door MijnProcedureB. Deze functie is weer aangeroepen door MijnProcedureA, welke zelf blijkbaar is geïnitieerd door btnTest2_Click(). Tevens zie je waarden die de parameters hebben.

Afbeelding 2. Call Stack Window
In de Threads-window zie je een overzicht van de threads die door je applicatie in gebruik zijn. Vanuit dit scherm kun je ze ook tijdelijk stoppen en later weer laten vervolgen. De Module-window is eigenlijk niets meer dan een lijstje met alle assemblies die door je applicatie gebruikt worden. Wanneer je grote buffers, strings of andere data wilt bekijken die niet geschikt zijn om in één van de Watch-windows te laten tonen, dan kun je eens kijken naar de Memory-window. Voor de echte hardcore programmeurs zijn er achtereenvolgens de Disassembly-window en de Registers-window, waarin je zelfs de assembly code kunt debuggen of kunt controleren welke registerwaarden er gewijzigd kunnen worden. De meeste van deze windows zijn te openen via het menu: Debug – Windows. Het is echter aan te raden om de bijbehorende toetscombinatie te gebruiken. Hierdoor kun je nog sneller tussen de diverse windows switchen.
Wanneer je je nu ook nog de sneltoetsen eigen maakt om binnen de debugcode te kunnen navigeren, bijvoorbeeld de sneltoetsen voor 'Step Into' of 'Step Over', dan staat niets je meer in de weg om je code op een heel efficiënte wijze te debuggen en stabieler te maken. Zoals je gezien hebt, biedt de IDE van Visual Studio .NET je voldoende hulpmiddelen.
Edit & Continue
Met name de Visual Basic ontwikkelaars waren teleurgesteld dat 'Edit & Continue' niet meer beschikbaar is binnen Visual Studio .NET. Met E&C bedoelen we de mogelijkheid om tijdens breakmode je code aan te passen en zonder opnieuw te hoeven compileren de code verder te laten uitvoeren. Zoals je echter in het vorige .NET Magazine hebt kunnen lezen is deze feature vanaf Visual Basic .NET 2005 weer terug. Hoewel ik persoonlijk erg blij ben dat dit weer beschikbaar komt, kent E&C ook een schaduwzijde. Ik zet een paar kanttekeningen op een rij. Met deze overwegingen in je achterhoofd houdend, hoop ik dat je E&C op die manier gaat gebruiken, waarvoor het uiteindelijk is bedoeld.
Het eerste probleem van 'shotgun-debugging' is wat men verstaat onder 'informality'; het spanningsveld tussen debuggen en testen. Wat vaak gebeurt is dat een programmeur een breakpoint zet aan het begin van een stuk code dat nadere aandacht verdient. Hij lost vaak meerdere problemen op een ad-hoc manier op, of denkt tenminste deze opgelost te hebben. Doordat hij stap-voor-stap door de code is gelopen, is de code in zijn ogen ook getest… Debuggen heeft tot doel om je code aan het werk te krijgen, terwijl testen tot doel heeft om je code te breken. Wanneer je het zo bekijkt staan deze twee werkzaamheden haaks op elkaar. Deze twee activiteiten tegelijkertijd doen, betekent eigenlijk dat geen beiden goed gedaan wordt.
Daarnaast kan E&C ook leiden tot meer 'trial and error' manier van programmeren. De programmeur lost een probleem op, maar begrijpt eigenlijk niet waarom de fout opgetreden is. Neem het volgende voorbeeld: De programmeur loopt tegen een foutmelding in een 'Loop' en denkt dat het te maken heeft met het wel of niet beginnen met een 0 als eerste item in een array. Dus hij plaatst een 'a+1' in de Loop en komt er vervolgens achter dat dit niet werkt. Dan maar een 'a-1' proberen, in de hoop dat het nu wel werkt. Ja.. het werkt. Hij denkt tijd bespaart te hebben, door niet opnieuw te hoeven starten. In feite snapt hij het hele concept niet en zal in andere delen van het project weer tegen dezelfde problemen aanlopen.
Ook het proberen om meerdere problemen tegelijkertijd op te lossen is niet altijd even verstandig. Wanneer er meerdere fixes nodig zijn voor het stukje code dat gedebugd wordt, dan kan men zich afvragen of het codeblok fundamenteel niet goed in elkaar zit. In zo'n geval is het beter om eens rustig over het stuk code na te denken.
Experimenten hebben uitgewezen dat een gemiddelde programmeur één nieuwe bug introduceert voor elke twee bugs die hij oplost. Zelfs de beste programmeurs introduceren een nieuwe bug voor elke vier bugs die opgelost worden. Het is overbodig te melden dat de ad-hoc manier van problemen oplossen tijdens E&C, om nog maar niet te spreken van het oplossen meerdere bugs tijdens dezelfde sessie, de kans op dit soort problemen alleen maar vergroot. Daarnaast blijkt uit onderzoeken dat de kans op het produceren van 'nieuwe' bugs toeneemt door aanpassingen van relatief kleine codeblokken. Aanpassingen in codeblokken van een tot vijf regels brengen risico's met zich mee. Dit zijn juist de aanpassingen die de programmeur besluit te doen tijdens E&C, omdat het overzichtelijke codestukken betreft. Bij aanpassingen aan codeblokken groter dan vijf regels neemt het risico van nieuwe bugs introduceren weer af. Dit komt waarschijnlijk doordat de programmeur beter oplet omdat het grotere aanpassingen betreft.
De Debug en Trace classes
Zoals je in het eerste deel van dit artikel hebt kunnen lezen biedt Visual Studio .NET je een groot aantal tools waarmee je erg efficiënt kunt debuggen. Op codegebied zijn er met de komst van het .NET Framework ook uitbreidingen gekomen. De Debug en Trace classes geven je de mogelijkheid om op relatief eenvoudige wijze informatie over de huidige toestand van je applicatie te loggen. Dit alles zonder te hoeven overschakelen naar breakmode. Vooral bij grote applicaties waar we spreken over honderden, zoniet duizenden coderegels, is het stap-voor-stap door je applicatie lopen om een logische fout op te sporen haast ondoenlijk. In dit soort gevallen kunnen de bovenstaande classes wellicht uitkomst bieden.
De grote lijnen
Met behulp van de Debug en de Trace classes kun je op basis van bepaalde condities informatie loggen. De informatie kun je tijdens het debuggen zien in de eerder besproken Output-window. Daarnaast wordt de informatie ook gestuurd naar de Listeners-collectie. Deze Listenerscollectie kan een of meerder classes bevatten die de ontvangen informatie vervolgens op een bepaalde manier verwerkt. Zo zijn er verschillende type Listeners, bijvoorbeeld een die de informatie wegschrijft naar een tekstbestand of naar het Eventlog. Ook zou je zelf een TraceListener kunnen maken die de informatie wegschrijft naar SQL- Server. Achteraf kun je deze informatie bestuderen en kan je inzicht geven in mogelijke oorzaken van problemen. De Debug en Trace classes zijn in principe gelijk. Ze hebben beiden dezelfde properties en methods en schrijven ook beide de informatie naar TraceListeners. Het enige verschil is, dat de Trace class ook zijn werk kan doen wanneer de applicatie gecompileerd is en uitgerold is bij de klant. Om de informatie ook werkelijk te kunnen loggen, staan een zestal methods tot je beschikking.
- Write. Schrijft altijd de tekst weg naar de Listeners collectie;
- WriteLine. Hetzelfde als Write, maar voegt ook een regeleinde toe;
- WriteIf. Schrijft de tekst weg naar de Listeners collectie, maar alleen wanneer de conditie een True geeft;
- WriteLineIf. Analoog aan WriteLine, schrijft deze methode de tekst alleen weg wanneer de expressie True is en voegt gelijk een nieuwe regel toe;
- Fail. Schrijft altijd de tekst weg naar de Listeners collectie, maar toont tevens een Messagebox met daarin de informatie;
- Assert. Schrijft de tekst weg naar de Listeners collectie, toont een Messagebox, maar doet dit alleen wanneer de conditie False is.
Het gebruik van deze methods is erg intuïtief en zal waarschijnlijk geen problemen opleveren. Bekijk de code maar eens in Listing 1.
Dim intA As Int32 = 1
Dim intB As Int32 = 2
' Write --> Altijd loggen
Trace.Write("Deze melding wordt gewoon gelogd")
' WriteIf --> Alleen loggen wanneer conditie = True
Trace.WriteIf(intA > intB, "Deze melding wordt niet gelogd")
' WriteLineIf --> Conditie = True + Carriage Return
Trace.WriteLineIf(intA < intB, "Deze melding wordt wel gelogd")
' Assert --> Conditie = False, toont ook MessageBox
Trace.Assert(intA > intB, "Assertboodschap", "Meer details")
|
| Codevoorbeeld 1. Voorbeelden Trace class methods |
Doordat Fail in alle gevallen èn Assert in sommige gevallen een MessageBox tonen, zijn deze methods eigenlijk alleen geschikt tijdens het debuggen. Ik denk niet dat het wenselijk is dat de gebruiker wordt geconfronteerd met een dialoogkader, boordevol met informatie, terwijl hij of zij daar feitelijk niets mee kan doen (Afbeelding 3).

Afbeelding 3. Het resultaat van de Assert opdracht
Om de informatie die je gaat wegschrijven wat overzichtelijker te maken, kun je met een aantal andere methods en properties teksten laten inspringen of weer laten terugspringen. Met behulp van de IndentSize property leg je vast hoe groot de inspringing telkens is. Vervolgens spring je in, door de Indent() method aan te roepen. Met UnIndent() spring je weer terug en met behulp van de IndentLevel property kun je opvragen in welke hiërarchische laag de tracing zich momenteel bevindt. Deze methods roep je aan tussen de diverse WriteXXX opdrachten.
TraceListeners
Alle informatie die gelogd wordt, wordt dus doorgegeven aan de TraceListeners collectie. Elk lid van de collectie ontvangt alle informatie. Wat de desbetreffende TraceListener vervolgens met die informatie doet is de verantwoordelijkheid van dat object zelf.
Wanneer de TraceListener geïnitialiseerd wordt, bestaat het direct al uit één lid, namelijk de 'DefaultTraceListener'. Doordat de DefaultTraceListener al aan de collectie is toegevoegd, zie je in de voorgaande voorbeelden de debuginformatie standaard in de Output-window verschijnen. In productie-omgevingen wil je echter niet naar de Outputwindow loggen, maar naar een tekstbestand of de Windows Eventlog. De keuze die je maakt hangt mede af van het platform waarop jouw applicatie gedraaid gaat worden. Vergeet niet dat de Windows Eventlog niet beschikbaar is op Windows 98. Indien je dit platform moet ondersteunen zal je keuze dus meestal een tekstbestand zijn. Ik spreek over 'meestal', omdat je er ook voor zou kunnen kiezen om zelf een TraceListener te maken die de data naar een Accessbestand logt. Je zou er zelfs nog voor kunnen kiezen om alle drie de Listeners aan de collectie toe te voegen. In Listing 2 zie je hoe aan één Trace-class twee TraceListeners worden toegevoegd.
Dim objTr As Trace
Dim objTrL1 As New TextWriterTraceListener("c:\MijnLog.log")
Dim objTrL2 As EventLogTraceListener
' Bepaal of EventLog al bestaat...
If objTrL2.EventLog.Exists("MijnLog") = False Then
'... zoniet maak de log aan
objTrL2.EventLog.CreateEventSource("Test", "MijnLog")
End If
' Maak de TraceListenerEventLog aan
objTrL2 = New EventLogTraceListener("MijnLog")
' Voeg de TextWriter en EventLogger toe
objTr.Listeners.Add(objTrL1)
objTr.Listeners.Add(objTrL2)
' Schrijf data weg en flush gelijk
objTr.WriteLine("Geklikt om: " & Now.ToLongTimeString)
objTr.Flush()
' Sluit de Listeners
objTrL1.Close()
objTrL2.Close()
' Verwijder Listeners uit de collectie
objTr.Listeners.Remove(objTrL1)
objTr.Listeners.Remove(objTrL2)
|
| Codevoorbeeld 2. Toevoegen van TraceListeners |
Nadat de Trace-class (objTr) is gedeclareerd, worden de twee TraceListeners gedeclareerd en geïnstantieerd. Indien nodig wordt eerst de Eventlog aangemaakt. De TraceListeners worden aan de collectie toegevoegd en er wordt data weggeschreven. Om de gegevens nu ook daadwerkelijk naar het bestand te schrijven dient er geflushed te worden. Als laatste worden de TraceListeners gesloten en uit de collectie verwijderd. Wellicht is het je opgevallen dat de Debug en Trace classes zogenaamde 'Shared' of 'Static' methods bevatten. Dit houdt in dat je het object niet hoeft te instantiëren met het keyword New, omdat het standaard al beschikbaar is. Een soort public en altijd aanwezig object. Dit is de reden waarom we de Listeners weer uit de collectie verwijderen, omdat wanneer we deze procedure voor een tweede keer zouden aanroepen, we een foutmelding krijgen omdat we hetzelfde object aan de collectie willen toevoegen.
Het resultaat kun je zien in afbeelding 4 en 5.

Afbeelding 4. Loggen naar de Eventlog

Afbeelding 5. Loggen naar een Tekstbestand
Trace Switches
Bij het debuggen wil je normaal gesproken alle data in de Output-window zien. Bij het tracen ligt dat anders; je wilt alleen tracen in bijzondere gevallen. Gelukkig kun je het functioneren van de Trace class van buitenaf beïnvloeden met behulp van het Application Config bestand. Dit doe je met zogenaamde 'Trace Switches'. Het .NET Framework kent twee typen: een 'BooleanSwitch' en de 'TraceSwitch'. De eerste geeft een Boolean waarde terug, dus 'aan' of 'uit'. Met het tweede type, dus de 'TraceSwitch' kun je een minimum niveau aangeven wanneer er getraced moet gaan worden. Er worden vijf niveaus of TraceLevels onderscheiden:
- Off. Er wordt niet getraced (0);
- Error. Summiere foutmeldingen (1);
- Warning. Summiere foutmeldingen en waarschuwingen (2);
- Info. Summiere foutmeldingen, waarschuwingen en andere korte boodschappen (3);
- Verbose. Alle bovenstaande punten uitgebreid met gedetailleerde boodschappen(4)
De waarde die ingesteld is op de TraceLevel-property kan men indirect uitlezen door een viertal read-only properties: TraceError, TraceWarning, TraceInfo en TraceVerbose. Wanneer de TraceLevel is ingesteld op TraceLevel.Info, dan is niet alleen Trace.TraceInfo True, maar ook de on- dergeschikte levels: TraceWarning en TraceError zijn dan beiden True. Je kunt zo'n App.Config bestand eenvoudig aan je project toevoegen door te kiezen voor 'Add New Item' en vervolgens voor 'Application Configuration File'. Voeg de sectie toe.
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <switches> <add name="MijnBooleanSwitch" value="1" /> <add name="MijnTraceSwitch" value="3" /> </switches> </system.diagnostics> </configuration> |
| Codevoorbeeld 3. BooleanSwitch in App.Config bestand |
We hebben twee switches aangemaakt, de BooleanSwitch heeft een waarde True. Het minimale level voor de TraceSwitch is ingesteld op TraceLevel.Info, oftewel 3. Let op: Het is jouw verantwoordelijkheid om in de code wel of niet te loggen. Je moet dus gebruik maken van bijvoorbeeld WriteIf of WriteLineIf en testen op de waarden van de BooleanSwitch of TraceSwitch. Zie listing 4.
Dim objBlnSwitch As New BooleanSwitch("MijnBooleanSwitch", "") Dim objTrcSwitch As New TraceSwitch("MijnTraceSwitch","")
' Value = 1 --> Wel loggen Trace.WriteLineIf(objBlnSwitch.Enabled = True, "Boolean")
' Value = 3, Error = 1, 1 < 3 -- > Wel loggen Trace.WriteLineIf(objTrcSwitch.TraceError = True, "Error")
' Value = 3, Verbose = 1, 4 niet < 3 -- > Niet loggen Trace.WriteLineIf(objTrcSwitch.TraceVerbose = True, "Verbose")
|
| Codevoorbeeld 4. Toepassen van Trace Switches |
Ik wil je één gave feature niet onthouden, namelijk het dynamisch aanmaken van een TraceListener. Dit kan ook met behulp het App.Config bestand. We breiden ons bestand uit met de Trace-sectie (Listing 5).
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <switches> <add name="MijnBooleanSwitch" value="1" /> <add name="MijnTraceSwitch" value="3" /> </switches> <trace autoflush="true"> <listeners> <add name="MijnLog" type="System.Diagnostics.TextWriterTraceListener" initializeData="c:\MijnLog.log" /> </listeners> </trace> </system.diagnostics> </configuration> |
| Codevoorbeeld 5. TraceListeners in App.Config bestand |
Zonder de code uit Listing 4 aan te passen wordt de Tracedata nu ook weggeschreven naar het tekstbestand op schijf.
Het .NET Framework in combinatie met Visual Studio .NET biedt je de tools en omgeving om zeer comfortabel te kunnen debuggen. Het is zeker de moeite waard om eens paar uur in te ruimen om alle mogelijkheden te verkennen. Het is een tijdsinvestering waarvan je geen spijt zult hebben.
Succes!
André