Click here to Skip to main content
15,867,141 members
Articles / Web Development / ASP.NET

Website Diagnostics Page to diagnose your ASP.NET Website

Rate me:
Please Sign up or sign in to vote.
4.94/5 (46 votes)
11 Jun 2011CPOL4 min read 243.4K   110   25
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.NET
<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.

C#
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:

C#
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.

C#
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.

C#
// 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:

C#
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:

C#
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.

C#
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.

Image 2

License

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


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

Comments and Discussions

 
GeneralGreate Pin
Aladár Horváth2-Sep-15 21:02
professionalAladár Horváth2-Sep-15 21:02 
GeneralNice work! Pin
Carlos Morales13-Aug-14 6:10
Carlos Morales13-Aug-14 6:10 
GeneralMy vote of 5 Pin
John Bracey11-Jul-13 21:41
John Bracey11-Jul-13 21:41 
QuestionSingle Page Pin
kiquenet.com24-Jun-13 2:06
professionalkiquenet.com24-Jun-13 2:06 
GeneralMy vote of 5 Pin
SagarRS7-Jun-13 1:37
professionalSagarRS7-Jun-13 1:37 
GeneralMy vote of 5 Pin
  Forogar  20-Jun-12 6:48
professional  Forogar  20-Jun-12 6:48 
QuestionUsing one single aspx file for diagnostics Pin
kiquenet.com3-Jun-12 21:41
professionalkiquenet.com3-Jun-12 21:41 
GeneralMy vote of 4 Pin
ham_gr806-Jul-11 20:55
ham_gr806-Jul-11 20:55 
GeneralRecommened one Pin
thatraja6-Oct-10 20:51
professionalthatraja6-Oct-10 20:51 
GeneralMy vote of 5 Pin
Md. Marufuzzaman15-Sep-10 20:58
professionalMd. Marufuzzaman15-Sep-10 20:58 
GeneralMy vote of 5 Pin
Abhinav S9-Sep-10 5:57
Abhinav S9-Sep-10 5:57 
GeneralGreat Article.. Pin
Sushant Joshi8-Sep-10 17:48
Sushant Joshi8-Sep-10 17:48 
GeneralRe CharSet=UTF8; Pin
Ajay Kale New31-Aug-10 22:45
Ajay Kale New31-Aug-10 22:45 
GeneralMy vote of 5 Pin
Puchko Vasili29-Aug-10 9:53
Puchko Vasili29-Aug-10 9:53 
GeneralMy vote of 5 Pin
jitendraZaa26-Aug-10 19:24
jitendraZaa26-Aug-10 19:24 
Nice tool to add in Collection.
Thanks for sharing
GeneralCreateUser and Delete it after Pin
kiquenet.com25-Aug-10 2:53
professionalkiquenet.com25-Aug-10 2:53 
GeneralMy vote of 5 Pin
Pete O'Hanlon23-Aug-10 23:19
subeditorPete O'Hanlon23-Aug-10 23:19 
GeneralGood stuff as usual man Pin
Sacha Barber23-Aug-10 22:00
Sacha Barber23-Aug-10 22:00 
GeneralMy vote of 5 Pin
Anurag Gandhi23-Aug-10 20:41
professionalAnurag Gandhi23-Aug-10 20:41 
GeneralMy vote of 5 Pin
thatraja23-Aug-10 19:24
professionalthatraja23-Aug-10 19:24 
GeneralThanks for sharing Pin
Hemant.Kamalakar23-Aug-10 19:21
Hemant.Kamalakar23-Aug-10 19:21 
Generalgood articel Pin
王皓23-Aug-10 15:37
王皓23-Aug-10 15:37 
GeneralMy vote of 5 Pin
engchin22-Aug-10 23:51
engchin22-Aug-10 23:51 
GeneralMy vote of 5 Pin
linuxjr22-Aug-10 10:40
professionallinuxjr22-Aug-10 10:40 
GeneralMy vote of 5 Pin
R. Giskard Reventlov21-Aug-10 22:02
R. Giskard Reventlov21-Aug-10 22:02 

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

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