5. Centralize the handling of errors, but make use of all contextual information.
Putting all of the above practices together gives you a richer context in which to report errors. The actual logic of reporting the error is something that you can implement in a centralized way. In this code example, a critical exception is caught. It is then passed to the central method called HandleError() to be read and logged. HandleError() subsequently calls LogEvent(), which is discussed in #6, below.
[Visual Basic]
Try
Dim sPath As String = "\\MYSERVER\myshare\myfile.txt"
System.IO.File.Delete(sPath)
Catch ex As System.IO.FileNotFoundException
HandleError(ex, EventImportance.Critical, EventID.FILE_ACCESS_ERROR, _
"[Delete failed: can't access path.]")
End Try
Public Sub HandleError(ByRef e As Exception, ByVal Importance As EventImportance, _
ByVal ID As EventID, ByVal ExtraInfo As String)
Dim sMessage As String
While Not e.InnerException Is Nothing
e = e.InnerException
sMessage += Chr(13) + e.Message + " [" + e.GetType().ToString() + "]"
End While
If ExtraInfo <> "" Then
sMessage += Chr(13) + ExtraInfo
End If
LogEvent(sMessage, Importance, ID)
End Sub
6. Report application errors to the Windows Event Logs
The Windows Event Logs provide an existing infrastructure that can be leveraged in the task of Enterprise error handling. It provides the same reliable error logging as used by the OS, and additionally offers sorting, filtering, and growth control of logs.
[Visual Basic]
Private Sub LogEvent(ByVal Msg As String, ByVal Type As EventImportance, ByVal ID As Integer)
Dim sEventLogName = "Application"
Dim elog As New System.Diagnostics.EventLog(sEventLogName)
elog.Source = GetAssemblyName()
If AlreadyLogged(elog, ID, Msg) Then Exit Sub
Dim EventType As System.Diagnostics.EventLogEntryType
Select Case Type
Case EventImportance.Critical
EventType = System.Diagnostics.EventLogEntryType.Error
Case EventImportance.Information
EventType = System.Diagnostics.EventLogEntryType.Information
Case EventImportance.Warning
EventType = System.Diagnostics.EventLogEntryType.Warning
End Select
Try
elog.WriteEntry(Msg, EventType, ID)
Catch ex As System.ComponentModel.Win32Exception
WriteFile("Windows Event Log [" + sEventLogName + "] is full! " + GetAssemblyName() _
+ " application failed to write error " + ID.ToString + ": " + Msg)
Catch ex As Exception
WriteFile("Unknown error writing to Windows Event Log [" + sEventLogName + "]!" + _
ex.Message + GetAssemblyName() + _
" application failed to write error " + ID.ToString + ": " + Msg)
End Try
End Sub
Private Function GetAssemblyName() As String
Dim assemb As Reflection.Assembly = Reflection.Assembly.GetExecutingAssembly()
Dim attr As System.Attribute
For Each attr In assemb.GetCustomAttributes(False)
If TypeOf attr Is System.Reflection.AssemblyTitleAttribute Then
GetAssemblyName = CType(attr, System.Reflection.AssemblyTitleAttribute).Title
End If
Next
End Function
Private Function AlreadyLogged(ByRef Log As System.Diagnostics.EventLog, _
ByVal ID As Integer, ByVal Message As String) As Boolean
Dim entr As System.Diagnostics.EventLogEntry
AlreadyLogged = False
For Each entr In Log.Entries
If entr.EventID = ID And entr.Message = Message Then
AlreadyLogged = True
Exit Function
End If
Next
End Function
7. Ensure all apps are able to report to the Windows Event Logs
With Windows 2003, security on the Event Logs has changed. You can now define custom Security Descriptors for each log. For example, the following registry key value defines access to the Application Event Log:
HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Applicaton\CustomSD
You need to configure carefully the various event logs used by your program so that it can log properly to the Event Log in all instances, and under the security context of any potential user. You also need to
ensure all users have access to read the above registry key or you won’t get far.
At the end of this value are tokens such as:
- (A;;0x1;;;DU) - allow domain users to read this event log
- (D;;0x7;;;BG) - deny built-in guest users (e.g. IUSER, ASPNET user) read, write, and clear
- (A;;0x3;;;IU) - allow interactive users to read and write to it
The middle unit of each token is a bitwise AND, in hex format, of 1:read, 2:write, 4:clear.
Other possible values for the last unit of each token include:
- BA (Built-in admin)
- SY (System)
- AN (Anonymous)
- SO (Server operators)
- SU (Service users)
- any SID representing a specific user or group on the machine or domain.