Fouten registreren in ASP.NET
Door Michiel van Otegem
1 augustus 2006
Een webapplicatie wordt potentieel door vele gebruikers bezocht. Dat daarbij af en toe
fouten ontstaan is haast onvermijdelijk. Met zoveel gebruikers wordt het echter lastig
om te achterhalen wat er fout gaat en waarom, om over het kunnen reconstrueren van de
fouten nog maar te zwijgen. Het is daarom uiterst belangrijk om fouten in een log op te
slaan, zodat er later een analyse gemaakt kan worden, en structurele fouten opgelost
kunnen worden.
In het vorige artikel uit deze serie heb ik foutafhandeling en opsporing binnen een
pagina besproken. Ook heb ik laten zien hoe je eenvoudig de configuratie in kunt stellen
om te verwijzen naar foutafhandelingspagina's, op basis van de verschillende fouten die
kunnen optreden. In een applicatie met veel gebruikers is het echter niet gezegd dat
een fout in een pagina de volgende keer ook optreedt, dat hangt sterk van de situatie
af. Wil je structurele fouten opsporen en oplossen, dan moet dus vastgelegd worden wat
er fout gaat. In ASP kan dit normaal gezien alleen in een database of apart bestand, en
dat is ronduit onhandig. Voor een beheerder is het veel makkelijker als alle logs via
hetzelfde mechanisme toegankelijk zijn. Precies om die reden was Windows NT al uitgerust
met de Event Log, en is dat ook nu een belangrijk gereedschap voor het gezond houden
van de server. In ASP was de Event Log echter niet aan te schrijven zonder speciaal
COM component. De Event Log is in het .NET Framework beschikbaar via de System.Diagnostics
namespace, en net als alle andere applicaties heeft ASP.NET hier gewoon toegang toe.
Ontwikkelaars kunnen er dus nu voor zorgen dat fouten netjes in de Event Log worden
opgeslagen, en daar is eigenlijk helemaal niet zoveel moeite voor nodig, zoals je kunt
zien in Figuur 1.
1: <%@ Page Language="VB" %>
2: <%@ Import Namespace="System.Diagnostics" %>
3: <script runat="server">
4: Sub Page_Load()
5: Dim strLogTekst = "Event demo " & DateTime.Now.ToString()
6: Dim ID As Integer = 50
7:
8: 'Log instellen
9: Dim el As EventLog = New EventLog()
10: el.MachineName = "."
11: el.Source = "EventLogDemo"
12: el.Log = "DemoLog"
13:
14: 'Log maken als het nog niet bestaat<
15: If Not EventLog.SourceExists(el.Source) Then
16: el.CreateEventSource(el.Source, el.Log)
17: End If
18:
19: 'Naar log schrijven
20: el.WriteEntry(strLogTekst, EventLogEntryType.Information, ID)
21: lblLog.Text = strLogTekst
22: End Sub
23: </script>
24: <html>
25: <body>
26: Log tekst:
27: <asp:label id="lblLog" runat="server"/>
28: </body>
29: </html>
Figuur 1, Pagina die naar de Event Log schrijft
De pagina in figuur 1 geeft als resultaat in de browser de tekst die naar de Event Log
geschreven is. Er wordt hier dus moedwillig naar de Event Log geschreven, zonder dat
er een fout optreedt. Dat kan handig zijn voor een audit log, bijvoorbeeld van succesvol
inloggen. Op de tweede regel wordt de System.Diagnostics Namespace geïmporteerd, zodat
we de classes kunnen gebruiken die nodig zijn als we naar de Event Log willen schrijven.
Het eerste wat de code doet is een tekst maken die naar de Event Log geschreven wordt,
en een bijbehorende foutcode. De tekst mag van alles zijn, maar is in principe bedoeld
voor menselijke lezers, dus het is handig om hierin een goede beschrijving van de fout
neer te zetten. De foutcode is in principe bedoeld voor andere programma's, bijvoorbeeld
om de Event Log te analyseren. Ook de foutcode mag je zelf bedenken, maar het is handig
om dit volgens bepaalde regels te doen, en eventueel om ze van te voren vast te leggen.
Het tweede argument van de functie WriteEntry bepaalt wat voor soort boodschap er naar
de Event Log geschreven wordt. De soort kan één van de volgende mogelijkheden van
EventLogEntryType zijn:
- Error
- Warning
- FailureAudit
- SuccesAudit
- Information
Error gebruik je natuurlijk voor fouten, terwijl een Warning meer bedoeld is om aan te
geven dat er potentieel problemen kunnen ontstaan. FailureAudit en SuccesAudit wordt
gebruikt voor gebeurtenissen die te maken hebben met de beveiliging, over het algemeen
het succesvol of onsuccesvol inloggen op het systeem. Information is voor puur informatieve
doeleinden, bijvoorbeeld het starten of stoppen van een applicatie.
Nu je weet hoe je naar de Event Log kunt schrijven, is de volgende stap ervoor zorgen
dat je fouten die je kunt voorzien goed afhandelt. Dat wil zeggen dat je de juiste
actie onderneemt als de fout optreedt, en vervolgens netjes opruimt. Als je dit niet
doet, kan de server op langere termijn problemen ondervinden van fouten die op zich
zelf onschuldig zijn. Operaties op een database kunnen bijvoorbeeld mis gaan. Het daarna
niet sluiten van de databaseverbinding kan nare gevolgen hebben, en het is dus zaak dit
soort problemen te voorkomen. In het .NET Framework, en dus in ASP.NET, hebben alle
talen dezelfde mechanismen om fouten af te handelen. Dit gaat via het zogenaamde
try-catch principe, waarin een blok code "geprobeerd" wordt en bij het optreden van
een fout code in werking treedt die de fout afhandelt. Als laatste kan er ook nog code
uitgevoerd worden ongeacht het resultaat. Figuur 2 laat code zien die gebruik maakt van
dit principe.
1: <%@ Page Language="VB" %>
2: <%@ import Namespace="System.IO" %>
3: <%@ import Namespace="System.Diagnostics" %>
4: <script runat="server">
5: Sub Page_Load()
6: Try
7: Dim sr As New StreamReader("c:\bestaatniet.txt")
8: lblFile.Text = sr.ReadToEnd()
9: sr.Close()
10: Catch ex As FileNotFoundException
11: Dim strLogTekst = "File not Found"
12: Dim ID As Integer = 50
13:
14: 'Log instellen
15: Dim el As EventLog = New EventLog()
16: el.MachineName = "."
17: el.Source = "EventLogDemo"
18: el.Log = "DemoLog"
19:
20: 'Log maken als het nog niet bestaat
21: If Not EventLog.SourceExists(el.Source) Then
22: el.CreateEventSource(el.Source, el.Log)
23: End If
24:
25: 'Naar log schrijven
26: el.WriteEntry(strLogTekst, EventLogEntryType.Information, ID)
27: lblFile.Text = "Error: File not Found"
28: Finally
29: 'Hier eventueel opruimen, nu niet nodig
30: End Try
31: End Sub
32: </script>
33: <html>
34: <body>
35: Log tekst:
36: <asp:label id="lblFile" runat="server"/>
37: </body>
38: </html>
Figuur 2, Pagina met Try-Catch constructie
In de pagina in figuur 2 wordt geprobeerd een bestand te openen en de inhoud daarvan
in lblFile te zetten. Als het bestand niet bestaat, wordt dat aan de Event Log gemeld
volgens hetzelfde principe als in figuur 1. Ook wordt er nog een bericht naar lblFile
geschreven. In het Finally blok staat in dit geval niets, maar hier zou je eventueel
kunnen opruimen na een fout. Het handige van op deze manier in de pagina een fout
afhandelen, is dat je de gebruiker niet persé op de hoogte hoeft te stellen van de fout.
Als je alternatieve actie kunt nemen, kun je het resultaat daarvan laten zien. Het
probleem met sommige fouten is echter dat je ze niet kunt voorzien. Ze gebeuren gewoon,
omdat er ineens iets onverwachts veranderd is. Dat soort fouten zijn vrijwel niet mooi
af te handelen, en in dat geval zul je dus moeten verwijzen naar een standaard foutpagina.
Je wil echter wel dat dit soort fouten worden geregistreerd in de Event Log, want dan
weet je later dat er wel iets fout is gegaan, en kun je eventueel actie ondernemen.
Voordat ASP.NET een gebruiker doorverwijst naar de opgegeven foutpagina, zal het eerst
Application_Error in het global.asax bestand uitvoeren. Dit bestand bevat
gebeurteniscode voor allerlei gebeurtenissen die buiten een pagina vallen.
Application_Error is specifiek bedoeld om fouten af te handelen. Figuur 3 laat zien
hoe dit eruit ziet.
1: <%@ Application language="VB" %>
2: <script runat="server">
3: Sub Application_Error(Sender As Object, E As EventArgs)
4: Dim ex As Exception = Server.GetLastError.GetBaseException()
5: Dim strMsg As String
6: Dim ID As Integer = 50
7:
8: strMsg = "MESSAGE=" & ex.Message & _
9: "\nSOURCE=" & ex.Source & _
10: "\nFORM=" & Request.Form.ToString() & _
11: "\nQS=" & Request.QueryString.ToString()
12:
13: 'Log instellen
14: Dim el As EventLog = New EventLog()
15: el.MachineName = "."
16: el.Source = "GlobalLogDemo"
17: el.Log = "DemoLog"
18:
19: 'Log maken als het nog niet bestaat
20: If Not EventLog.SourceExists(el.Source) Then
21: el.CreateEventSource(el.Source, el.Log)
22: End If
23:
24: 'Naar log schrijven
25: el.WriteEntry(strLogTekst, EventLogEntryType.Information, ID)
26: End Sub
27: </script>
Figuur 3, Global.asax bestand voor het centraal afhandelen van fouten
In figuur 3 is het meeste al bekend. Wat anders is, is de opbouw van de foutboodschap.
Omdat de Event Log maar beperkte informatie opslaat, is het zaak om de boodschap zo
uitgebreid mogelijk te maken. In dit geval wordt daarom de boodschap van de
oorspronkelijke fout en de bron daarvan meegestuurd in de boodschap. Ook worden de
gegevens die de browser meegestuurd heeft, zowel die uit een formulier als in de URL,
bij de aanvraag meegestuurd. Die eerste twee gegevens kun je ophalen door het Exception
(fout) object op te vragen van de laatst voorgekomen fout (de fout die er voor heeft
gezorgd dat deze code wordt uitgevoerd). De gegevens uit het formulier en de URL moet
je los opvragen.
Conclusie
Foutafhandeling is een belangrijk aspect van elke applicatie. Met behulp van standaard
functies in het .NET Framework en ASP.NET, is het makkelijk om fouten af te handelen
en zonodig te registreren. Het is vervolgens aan de systeembeheerder en de ontwikkelaar
samen om de gegevens die daardoor beschikbaar komen te gebruiken om de applicatie en
de server gezond te houden.
De code in dit artikel kun je downloaden via de Windows & .NET Magazine website.
Hierbij zit ook een pagina om de Event Log via het web uit te lezen (vanwege de
complexiteit en lengte hier niet besproken).
Dit artikel is eerder verschenen in Windows & .NET Magazine Benelux, november 2002 (huidige naam NetOpus)
|