Some developers have system administrators and never log into a server. Most of us are the system administrators (regardless of how qualified we are) and might spend as much time as we do coding reading Event Viewer logs. Fortunately for us, the .Net framework doesn't care if you're an ASP.NET app, WinForms, or WPF - you can use the Event Logs to make tracking problems easier.
I'm not going to cover security here, but reading and writing to the Event Logs requires proper permissions, and these may change based on the Log and Category you are attempting to access.
Since I'm going to make a simple ASP.NET Event Viewer, I'll start out by creating a quick business object to wrap the underlying framework classes.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ComponentModel;
[DataObject(true)]
public class EventViewer {
public EventViewer() { }
[DataObjectMethod(DataObjectMethodType.Select, true)]
public EventList GetLog(String logName) {
EventList results = new EventList();
EventLog log = new EventLog();
log.Log = logName;
foreach (EventLogEntry entry in log.Entries)
results.Add(new EventRecord(entry));
results.Reverse();
return results;
}
public EventList GetLog(String logName, Int32 maximumRows, Int32 startRowIndex) {
EventList results = new EventList();
EventLog log = new EventLog();
log.Log = logName;
int startIdx = log.Entries.Count - (1 + startRowIndex);
int endIdx = startIdx - maximumRows + 1;
for (int i = startIdx; i >= 0 && i >= endIdx; i--) {
results.Add(new EventRecord(log.Entries[i]));
}
return results;
}
public int GetLogCount(String logName) {
EventLog log = new EventLog();
log.Log = logName;
return log.Entries.Count;
}
}
public class EventList : List<EventRecord> { }
public class EventRecord {
public EventRecord() { }
public EventRecord(EventLogEntry entry) {
Timestamp = entry.TimeGenerated;
Source = entry.Source;
EventID = entry.InstanceId;
Message = entry.Message;
UserName = entry.UserName;
EventType = entry.EntryType.ToString();
}
private DateTime _timestamp;
public DateTime Timestamp {
get { return _timestamp; }
set { _timestamp = value; }
}
private String _source;
public String Source {
get { return _source; }
set { _source = value; }
}
private long _eventID;
public long EventID {
get { return _eventID; }
set { _eventID = value; }
}
private String _message;
public String Message {
get { return _message; }
set { _message = value; }
}
public String MessagePreview {
get {
if (_message != null)
return _message.Split("\n.".ToCharArray())[0];
else return String.Empty;
}
}
private String _eventType;
public String EventType {
get { return _eventType; }
set { _eventType = value; }
}
private String _username;
public String UserName {
get { return _username; }
set { _username = value; }
}
}
The System.Diagnostics namespace holds the classes and interfaces for working with the event logs. To get an instance of a log, create an instance of EventLog and set the EventLog.Log property to the name of the log you wish to read or write to. "Application", "System", and "Security" are common but not the only options - applications (event yours! )can create custom logs. My example here is read only, but if you wanted to write an entry you would call EventLog.WriteEntry which has a number of overloads.
The logs are accessed in chronological order so you'll want to reverse them to have the newest entry first. I've provided two versions of GetLog, the second is intended to support the paging features of ObjectDataSource. If the DataObject attributes are new to you, check out my previous article GridView and ObjectDataSource with custom objects.
With these objects in place, it's a simple matter to wire up an aspx page to browse the logs:
<asp:ObjectDataSource ID="odsEventViewer" runat="server" EnablePaging="True"
SelectCountMethod="GetLogCount" SelectMethod="GetLog" TypeName="EventViewer">
<SelectParameters>
<asp:ControlParameter ControlID="ddlEventLog" DefaultValue="Application"
Name="logName" PropertyName="SelectedValue" Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
<h2>
Select Event Log:
<asp:DropDownList ID="ddlEventLog" runat="server" AutoPostBack="True"
OnSelectedIndexChanged="ddlEventLog_SelectedIndexChanged">
<asp:ListItem Selected="True">Application</asp:ListItem>
<asp:ListItem>System</asp:ListItem>
</asp:DropDownList>
</h2>
<asp:MultiView ID="mvEventViewer" runat="server" ActiveViewIndex="0">
<asp:View ID="vList" runat="server">
<asp:GridView ID="gvEvents" runat="server" AllowPaging="True" AutoGenerateColumns="False"
DataSourceID="odsEventViewer" OnSelectedIndexChanged="gvEvents_SelectedIndexChanged">
<Columns>
<asp:BoundField DataField="Timestamp" />
<asp:BoundField DataField="EventType" />
<asp:BoundField DataField="Source" />
<asp:BoundField DataField="MessagePreview"/>
<asp:CommandField SelectText="Details" ShowSelectButton="True" />
</Columns>
<PagerSettings Mode="NumericFirstLast" />
</asp:GridView>
</asp:View>
<asp:View ID="vDetail" runat="server">
<asp:LinkButton ID="Back" runat="server" CommandArgument="vList"
CommandName="SwitchViewByID">Back to List</asp:LinkButton><br />
<asp:DetailsView ID="dvEvent" runat="server" AutoGenerateRows="False" AllowPaging="True"
DataSourceID="odsEventViewer">
<Fields>
<asp:BoundField DataField="Timestamp"/>
<asp:BoundField DataField="EventType"/>
<asp:BoundField DataField="Source"/>
<asp:BoundField DataField="UserName"/>
<asp:BoundField DataField="EventID"/>
<asp:TemplateField>
<ItemTemplate>
<pre style="overflow: auto;"><asp:Label
ID="Label1" runat="server" Text='<%# Bind("Message") %>'/></pre>
</ItemTemplate>
</asp:TemplateField>
</Fields>
</asp:DetailsView>
</asp:View>
</asp:MultiView>
Pretty basic and straight forward CRUD "D" 101 here. The ObjectDataSource EnablePaging="True" will call the GetLog overload passing in the current start index and number of rows to return. A minor bit of event handling brings it all together:
protected void ddlEventLog_SelectedIndexChanged(object sender, EventArgs e) {
gvEvents.PageIndex = 0;
mvEventViewer.SetActiveView(vList);
}
protected void gvEvents_SelectedIndexChanged(object sender, EventArgs e) {
dvEvent.PageIndex = gvEvents.SelectedRow.DataItemIndex;
dvEvent.DataBind();
mvEventViewer.SetActiveView(vDetail);
}
Now, when you fill the event log with errors you can at least say you can read them back as well!
Posted By Mike On Tuesday, October 16, 2007
Filed under asp.net developer |
Comments (3)
Jan Hussaarts
-
Wednesday, March 26, 2008
4:44:52 PM
Just what I needed!
Thanks
Jason Dufair
-
Wednesday, December 14, 2011
11:04:34 AM
This was really helpful. Thanks for the great, concise code.