Click here to Skip to main content
Click here to Skip to main content

Website Diagnostics Page to diagnose your ASP.NET Website

By , 11 Jun 2011
Rate this:
Please Sign up or sign in to vote.
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


Comments and Discussions

 
GeneralRecommened one Pinmemberthatraja6-Oct-10 20:51 

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.

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