Click here to Skip to main content
Licence CPOL
First Posted 21 Aug 2010
Views 30,637
Downloads 0
Bookmarked 89 times

Website Diagnostics Page to diagnose your ASP.NET Website

By | 11 Jun 2011 | Article
A self-diagnostics page that runs through your web.config and confirms all the settings are correct, is a quick and easy way to identify environment problems after configuration change or production deployment.
Diagnostics_page.gif

Introduction

Whenever you change web.config file or deploy your website on a new environment, you have to try out many relevant features to confirm if the configuration changes or the environment is correct. Sometimes, you have to run a smoke test on the website to get the peace of mind. Moreover, if some external database, webservice or network connectivity is down, it takes time to nail down exactly where the problem is. Having a self-diagnostics page on your website, like the one you see on your printer, can help quickly identify exactly where the problem is. Here’s a way how you can quickly create a simple self-diagnostics page in a single page using straightforward AJAX technologies. This diagnostics page tests for common configuration settings like connection string, ASP.NET Membership configurations, SMTP settings, <appSettings> file paths and URLs and some application specific settings to confirm if the configuration is all correct.

Building the Self-diagnostics Page Framework

I have used the UpdatePanel and an AJAX Timer to perform each diagnostics step. Every 500ms, it performs an async postback to the page and the page carries out one particular diagnostics step at a time. This way, even if the whole diagnostics process takes a lot of time to complete, it won't time out the page and there won't be a large delay showing the page initially.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true"
        ScriptMode="Release">
    </asp:ScriptManager>
    <asp:UpdatePanel runat="server" UpdateMode="Conditional">
        <ContentTemplate>
            <asp:Timer runat="server" ID="RefreshTimer" Interval="500" 
		Enabled="true" OnTick="RefreshTimer_Tick" />
            <h1>
                Self Diagnostics</h1>
            <h2>
                Database Configuration</h2>
            <ul>
                <li>
                    <asp:Label CssClass="inprogress" 
		    Text="Can read and write to database using the connection string."
                        runat="server" ID="ConnectionStringStatusLabel" /></li>
            </ul>

The AJAX Timer fires the RefreshTimer_Tick on the server. It then executes one step at a time. For each diagnostics step, I have put a Label that shows the step title. The Label is updated with error and suggestions when it fails. Otherwise, it's marked with a CSS class that shows the tick sign.

protected void RefreshTimer_Tick(object sender, EventArgs e)
    {
        this._Tasks[this.CurrentTaskNo]();
        
        this.CurrentTaskNo++;
        if (this.CurrentTaskNo == this._Tasks.Length)
        {
            this.RefreshTimer.Enabled = false;
        }
    }

The _Tasks is an array of delegates. Each step is nothing but a function that performs certain diagnostics step. Once you build the _Tasks with an array of functions, this RefreshTimer_Tick executes one function at a time.

The list of diagnostics steps are defined in the OnInit event:

 protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        this._Tasks = new Action[] 
        {
            () => TestConnectionString(),
            () => TestMembershipAPI(),
            () => TestWrite(),
            () => TestPaths(),
            () => TestUrls(),
            () => TestSMTP(),
            () => TestAppSettings(),
            () => TestAnonTemplateUser(),
            () => TestRegTemplateUser()
        };
    } 

That’s it. The framework is very simple. The complexity is in the diagnostics steps.

Testing ASP.NET Membership Settings

ASP.NET Membership has three configuration settings that should be tested – the Membership API, the Profile API and the Role Manager. The diagnostics function tries to create a new user, a new profile and checks for a certain role to exist in order to confirm the membership settings are all correct. You can certainly change the logic to perform test that suits your need.

private void TestMembershipAPI()
{
    try
    {
        // Test Membership API
        try
        {
            var newUser = Membership.CreateUser
		(Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
        }
        catch (Exception x)
        {
            MarkAsFail(MembershipLabel, x.Message, 
                "Probably wrong connection string name in 
		<membership> block in web.config. 
		Don't use the Entity Framework Connection string");

            return;
        }

Similarly, you can test the Profile Provider to confirm whether the profile provider settings are all correct.

// Test Profile API
try
{
    UserProfile newProfile = ProfileCommon.Create
			(Guid.NewGuid().ToString()) as UserProfile;
    if (newProfile == null)
    {
        MarkAsFail(MembershipLabel, "Cannot create new user.",
        	"You might have wrong connection string name in <profile> block. 
	Ensure it's the simple connection string, not the entity framework one. 
	Or you may not have the 'inherits=\"Dropthings.Web.Framework.UserProfile\" 
	attribute in the <profile> block.");
        return;
    }

    newProfile.IsFirstVisit = false;
    newProfile.Fullname = "Test";
    newProfile.Save();
}
catch (Exception x)
{
    MarkAsFail(MembershipLabel, x.Message,
        "Probably wrong connection string name in <profile> block in web.config. 
	Don't use the Entity Framework Connection string");
    return;
}

Here the test code is very specific to my project. You can change this to test whatever way you want.

Test App_Data has Write Permission

Sometimes, we forget to give the ASPNET account or NETWORK SERVICE account write permission to the App_Data folder. The following test confirms that the app_data folder has the right permissions:

private void TestWrite()
{
    try
    {
        File.AppendAllText(Server.MapPath("~/App_Data/" + Guid.NewGuid().ToString()), 
		Guid.NewGuid().ToString());

        MarkAsPass(AppDataLabel);
    }
    catch (Exception x)
    {
        MarkAsFail(AppDataLabel, x.Message, "Give read, write, 
	modify permission to NETWORK SERVICE account to App_Data folder.");
    }
}

It performs the test by creating a random file. It only tests if create and write permission is there. It does not test if delete permission is there. You can add it if it suits your project by trying to delete the randomly created file.

Testing File Paths in <appSettings> Block

Most of the time, we put the file paths in the <appSettings> block. The following test confirms if all the relative paths are correct and exist:

private void TestPaths()
{
    bool allPathOK = true;
    foreach (string key in ConfigurationManager.AppSettings.AllKeys)
    {
        string value = ConfigurationManager.AppSettings[key];

        if (value.StartsWith("~/"))
        {
            string fullPath = Server.MapPath(value);
            if (!Directory.Exists(fullPath) && !File.Exists(fullPath))
            {
                MarkAsFail(FilePathLabel, "Invalid path: " + key + "=" + 
				fullPath, string.Empty);
                allPathOK = false;
            }
        }
    }

    if (allPathOK)
        MarkAsPass(FilePathLabel);
        
}

It loops through all the <appSettings> entries and checks if any of the entries has a value that’s in relative file path format. If it finds one, it converts it to local absolute path and confirms the path exists.

Testing External URLs are Reachable in <appSettings> Block

Just like file path, you can test if the external URLs are all working or not.

private void TestUrls()
{
    bool allUrlOK = true;

    foreach (string key in ConfigurationManager.AppSettings.AllKeys)
    {
        string value = ConfigurationManager.AppSettings[key];
        Uri uri;
        if (Uri.TryCreate(value, UriKind.Absolute, out uri))
        {
            // Got an URI, try hitting
            using (WebClient client = new WebClient())
            {
                try
                {
                    client.DownloadString(uri);
                }
                catch (Exception x)
                {
                    MarkAsFail(URLReachableLabel, x.Message, 
			"Unreachable URL: " + key + "=" + uri.ToString());
                    allUrlOK = false;
                }
            }
        }            
    }

    if (allUrlOK)
        MarkAsPass(URLReachableLabel);
}

The test runs through all <appSettings> entries, checks if any of the values is in URL format. If it is, it tries to perform an HTTP GET on the URL.

Conclusion

Self diagnostics entirely depends on the type of application you have. The above code shows you some examples that you can start with. You should put some application specific tests to confirm the external webservices, databases, network shares, file paths and important transactions are executing properly. Whenever you have some fault reported on your website, you can first run the self-diagnostics page to confirm if the settings are all correct or not. Then you can spend time checking for specific problem areas. Such a self-diagnostics page helps eliminate manual investigation time and helps you identify problem areas quickly.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Omar Al Zabir

Architect
BT, UK (ex British Telecom)
United Kingdom United Kingdom

Member

I am: Chief Architect, SaaS Platform, BT (ex British Telecom). Visual C# MVP '05-'07, ASP.NET/IIS MVP '08-'12
I was: Co-founder & CTO, Pageflakes(www.pageflakes.com)
I like: Performance and Scalability Challenges.
My Book: Building a Web 2.0 portal using ASP.NET 3.5. Also on Amazon
My Blog: http://omaralzabir.com
My Specialization: Web 2.0 Rich AJAX Applications, Level 4 SaaS, Performance and Scalability of Web Apps.
My Email: OmarALZabir at gmail dot com
 
Follow Me: twitter.com/omaralzabir
 
My Projects:
Open Source Web 2.0 AJAX Portal
PlantUML Editor - Super fast UML editor
Smart UML - Freehand UML Designer
RSS Aggregator both Outlook and Standalone
Store Front in JSP but ASP.NET style
 
My Articles:
Top 10 caching mistakes
99.99% Available Production Architecture
Build GoogleIG like Ajax Start Page in 7 days
10 ASP.NET Performance and Scalability Secrets
ASP.NET AJAX under the hood secrets
UFrame: UpdatePanel and IFRAME combined
Fast ASP.NET web page loading
Fast Streaming AJAX Proxy
Using COM safely inside "using" block without requiring interop assembly
Implementing Word Like Automation Model
Distributed Command Pattern

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 4 Pinmemberham_gr8020:55 6 Jul '11  
GeneralRecommened one Pinmemberthatraja20:51 6 Oct '10  
GeneralMy vote of 5 PinmvpMd. Marufuzzaman20:58 15 Sep '10  
GeneralMy vote of 5 PinmemberAbhinav S5:57 9 Sep '10  
GeneralGreat Article.. PinmemberSushant Joshi17:48 8 Sep '10  
GeneralRe CharSet=UTF8; PinmemberAjay Kale New22:45 31 Aug '10  
GeneralMy vote of 5 PinmemberPuchko Vasili9:53 29 Aug '10  
GeneralMy vote of 5 Pinmembershivasoft19:24 26 Aug '10  
GeneralCreateUser and Delete it after Pinmemberalhambra-eidos2:53 25 Aug '10  
GeneralMy vote of 5 PinmvpPete O'Hanlon23:19 23 Aug '10  
GeneralGood stuff as usual man PinmvpSacha Barber22:00 23 Aug '10  
GeneralMy vote of 5 PinmemberAnurag Gandhi20:41 23 Aug '10  
GeneralMy vote of 5 Pinmemberthatraja19:24 23 Aug '10  
GeneralThanks for sharing PinmemberHemant.Kamalakar19:21 23 Aug '10  
Generalgood articel Pinmember王皓15:37 23 Aug '10  
GeneralMy vote of 5 Pinmemberengchin23:51 22 Aug '10  
GeneralMy vote of 5 Pinmemberlinuxjr10:40 22 Aug '10  
GeneralMy vote of 5 Pinmemberdigital man22:02 21 Aug '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web04 | 2.5.120517.1 | Last Updated 11 Jun 2011
Article Copyright 2010 by Omar Al Zabir
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid