ASP.NET: Working with the Event Log

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)

Submit this story to DotNetKicks   

Jan Hussaarts - Wednesday, March 26, 2008 4:44:52 PM

Just what I needed!
Thanks

Lorenzo Jimenez - Tuesday, April 22, 2008 6:01:12 PM

Just Excelent!
Thanks

Jason Dufair - Wednesday, December 14, 2011 11:04:34 AM

This was really helpful. Thanks for the great, concise code.

Leave a comment



Your name:
 

Your email (not shown):
 
Will display your Gravatar image.

Your website (optional):



About Michael

Michael C. Neel, born 1976 in Houston, TX and now live in Knoxvile, TN. Software developer, currently .Net focused. Board member of ETNUG and organizes CodeStock, East Tennessee's annual developers conference. .Net speaker, a Microsoft ASP.NET MVP and ASPInsider. Co-Founder of FuncWorks, LLC and GameMarx.

Proud father of two amazing girls, Rachel and Hannah, and loving husband to Cicelie who inflates and pops his ego as necessary.

 Subscribe to ViNull.com |  Comments

Follow me on Twitter | Contact Me

Related Posts

Session, ForEach, and a ShallowCopy walk into a bar...

Before we get started, let's play "questions you only hear during an interview."   Are the contestants ready?  Good, here is the question:  ... Read more

ASP.NET: Things I've Learned

I've learned that web.config will inherit across applications on the same site. I've learned that this is true, even if the child application is a virtual ... Read more

ASP.NET: Cannot use a leading .. to exit above the top directory

This is a really quick post, mostly for myself so the next time I have this issue I can find the answer (yes, I often search my blog before google). The ... Read more

XNA 3D Primer Published – Get a free copy!

In June of 2006 I officially became a professional author when ASP.NET Pro published my article “Google Can You Hear Me?”.  (So eager was I to be ... Read more

ASP.NET: Creating a UserControl with Child Content

I love ASP.NET User Controls, aka “ascx” files.  These little guys are great for reusable content and dividing up the components of a website.  ... Read more

XNA 3D Primer by Michael C. Neel

XNA 3D Primer by Michael C. Neel
Buy Now: [ Amazon ] [ Wrox ]

GameMarx

CodeStock

ASPInsiders Member

ETNUG Member