Specifying the Logging Requirements
While it is easy to be overwhelmed by the Health Monitoring classes and their myriad interactions, we're going to pick our way through the apparent complexity by focusing on creating a useful, robust, and extensible logging facility. To do so, let's delineate our requirements.
To begin, an event is defined as an error, exception, unexpected outcome, or notable occurrence that we want to keep track of. (Later, not shown in this article, we might differentiate two or more types of events--e.g., audit events and errors.)
Our logging module must:
- Track all events to a database table
- Send an email for a selected subset of events
- Ensure that the system has a non-measurable effect on performance
- Ensure that the system is highly extensible so that we can add new events, tweak the throttling and buffering (to manage the performance impact), change where events are logged, etc.
Creating the Test Bed
To demonstrate how you can implement logging that stores the required information both in a database and, optionally, send email for critical errors, you'll need a program that can generate exceptions, errors, and so forth. We'd like our sample application to be simple (so that we can focus on the logging) but at least marginally realistic.
Create a new ASP.NET application and call it Logging. Click on Website-->ASP.NET Configuration to open the Web Site Administration Tool, and click on Security to set up Forms-based security (from the Internet). Create a single user.
Close the tool and ensure that your web.config file has the following lines (if not, add them by hand):
<authentication mode="Forms" /> <membership> <providers> <remove name="AspNetSqlMembershipProvider"/> <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider, System.Web, Version=126.96.36.199, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" connectionStringName="LocalSqlServer" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="10" minRequiredPasswordLength="1" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/> </providers> </membership>
Double-check that the name of the connection string listed is
LocalSqlServer and that inside your App_Data folder a file has been created named ASPNETDB.MDF. You should be able to examine the tables for this database using the Server Explorer window, as shown in Figure 2.
Figure 2. Examining the Personalization Tables
To keep the example simple, maintain the name Default.aspx. You'll create a GridView that is bound to the Person.Contact table in the sample AdventureWorks database that ships with SQL Server 2005 (or you can download it from Microsoft). The only extras are that you'll allow the user to put in the starting letters of the user's last name to narrow the results, and you'll add a button that will force an exception, as shown in Figure 3.
Figure 3. The Grid you'll build
To create this grid, follow these relatively easy steps:
- Drag a GridView control onto your .aspx page and name it ContactGridView.
- Use the smart tag to create a new DataSource. Point the data source to your AdventureWorks database, saving the connection string, and use the following SQL statements for Select, Update, Insert, and Delete:
SELECT [ContactID], [FirstName], [LastName], [EmailAddress], [Phone] FROM [Person].[Contact] where LastName LIKE @LastName + '%' order by LastName UPDATE [Person].[Contact] SET [FirstName] = @FirstName, [LastName] = @LastName, [EmailAddress] = @EmailAddress, [Phone] = @Phone WHERE [ContactID] = @ContactID UPDATE [Person].[Contact] SET [FirstName] = @FirstName, [LastName] = @LastName, [EmailAddress] = @EmailAddress, [Phone] = @Phone WHERE [ContactID] = @ContactID INSERT INTO [Person].[Contact] ([FirstName], [LastName], [EmailAddress], [Phone]) VALUES (@FirstName, @LastName, @EmailAddress, @Phone) DELETE FROM [Person].[Contact] WHERE [ContactID] = @ContactID
The square brackets in these SQL statements are not strictly necessary, but they are used to avoid problems with spaces within object names.
The fastest way to get this right is to let the control create the statements for you. This will let you use the tool to create the WHERE clause (using the text box as the source for the parameter) and to generate the Insert, Update, and Delete statements.
You will then need to edit each statement to prepend each table name with the schema names used in this latest version of the AdventureWorks database (e.g., [Person].[Contact]). You'll also need to edit the Select statement to change the WHERE clause to use the LIKE statement (which lets you use the wild card character %).
Below the grid, add the Label, TextBox, and two Buttons, using this markup:
<asp:Label ID="LastNamePrompt" runat="server" Text="Last name starts with: " /> <asp:TextBox ID="LastName" runat="server" Width="96px" /> <asp:Button ID="Search" runat="server" Text="Search" /> <asp:Button ID="ThrowException" runat="server" OnClick="ThrowException_Click" Text="Divide by zero" />
Run the program and make sure that you can retrieve and edit data from the appropriate table. Great.