Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

Displaying useful Configuration and Status information in an ASP.NET app


:P
On this page:

After my post about Impersonation Security I got a number of requests for more information about my application’s information screen that displays some basic statistics and performance counters. Here’s what that page looks like again:

 

Application Status display form

 

Notice that I actually dump trace information onto the page on the bottom so I can see a number of common server settings that ASP.NET natively dumps out. While normally it's a bad idea to do this, this information page is meant as an admin page and requires a login, so I consider this safe to do in this scenario.

 

The actual form content itself contains some informational data as well as admin links that go off and retrieve error information, simulate an error (so you can see what error behavior looks like in your app and to check whether logging is working correctly).

 

The information on the bottom of the form contains plain values taken straight out of the environment and I’m actually sticking them onto the page directly using classic ASP tags:

 

<hr>

<p><b>.Net Framework Version: </b>

      <br>

      <%= Environment.Version.ToString() %>

</p>

<p><b>System Account running App:</b><br>

      <%= Environment.UserName %>

<p><b>Logged on User:</b><br>

      <%= User.Identity.Name == "" ? "Anonymous User" : User.Identity.Name %>

</p>

<p><b>AppDomain Id:</b><br>

      <%= AppDomain.CurrentDomain.FriendlyName %>

</p>

<p><b>Thread Id:</b><br>

      <%= AppDomain.GetCurrentThreadId()  %>

</p>

 

Framework version is pretty important, especially these days when I have V 2.0 of the framework installed and I’m never sure exactly where it wiggled its way into automatically <g>.

 

The usernames I discussed yesterday. Environment.UserName returns the system account that the app is running under, while Environment.UserName returns the account that ASP.NET has authenticated. In most cases the ‘system’ account will be ASPNET or NETWORK SERVICE or whatever AppPool Identity you have configured in IIS 6. This value could be the same as the Identity if you use enable Impersonation in your web.config. For more detail on this see yesterday’s post.

 

The AppDomain Id I like to display because I frequently do demos that demonstrate the ASP.NET pipeline and processing model and it demonstrates that threads frequently change, but the AppDomain stays steady unless the Web application restarts itself. This is not a commonly useful value, but can be useful if you are trying to figure out if and why an app is restarting.

 

Performance Counters

It’s been a while since I created this code, but I remember that it wasn’t real obvious to retrieve counters beyond the simple counters that almost every example that I found online uses. There countless examples that show the basic stuff like reading a static value, but nothing that showed how to read say CPU utilization which is what is known as a range value that is sampled over a sample period. essentially you need to resample the value for a few times and then calculate an average. As it is I’m not even sure whether the approach I use below is the right way to do this but it appears to work correctly giving a rough idea of CPU utilization. Basically I want to see whether the server is maxed out and that I possibly need to take corrective action.

 

Couple warnings about Perf Counters though. In ASP.NET 1.1 a number of the counters I wanted to include didn’t return valid values. When checking with the ASP.NET team I was flat out told that many of the ASP.NET perf counters don’t work correctly in 1.1. This will apparently be fixed in 2.0, but I haven’t checked.

 

Sql Counters too seem to be flaky if you restart the SQL Server or shut down the Sql Server Server Agent. This is usually not a problem on a live server, but I’ve seen screwy numbers on my dev server and the only way to get the numbers to reset themselves seemed to be to restart the machine.

 

Anyway, here’s the code I use to generate the Perf counters you see on the page above.

 

private void Page_Load(object sender, System.EventArgs e)

{

      dt = new DataTable();

      dt.Columns.Add("Counter",typeof(string));

      dt.Columns.Add("Value",typeof(float));

 

      this.AddCounter("Processor", "% Processor Time", "_Total","CPU Utilization",100);

 

      this.AddCounter("SQLServer:General Statistics", "User Connections",null,

                    "Sql User Connections");

     

//                this.AddCounter(".NET CLR Data", "SqlClient: Current # pooled connections","_global_");

//                this.AddCounter(".NET CLR Data", "SqlClient: Current # connection pools","_global_");

      this.AddCounter(".NET CLR Data", "SqlClient: Total # failed connects","_global_");

 

      this.AddCounter("ASP.NET", "Request Wait Time",null,

            "ASP.Net Request Wait Time");

 

      this.AddCounter("ASP.NET","Requests Current",null,

            "ASP.Net Current Requests");

 

      this.AddCounter("ASP.NET", "State Server Sessions Active",null,

            "State Server Sessions");

 

      this.CounterList.DataSource = this.dt;

      this.CounterList.DataBind();

}

 

protected void AddCounter(string Counter,string SubCounter)

{

            this.AddCounter(Counter,SubCounter,null,null);

}          

protected void AddCounter(string Counter,string SubCounter, string Instance)

{

      this.AddCounter(Counter,SubCounter,Instance,null);

}

protected void AddCounter(string Counter,string SubCounter, string Instance,

      string Key)

{

      this.AddCounter(Counter,SubCounter,Instance,Key,1);

}

 

protected void AddCounter(string Counter,string SubCounter, string Instance,

                                  string Key,int Samples)

{

      float Value = 0.00F;

      try

      {

            if (Instance != null)

                  this.Counter = new PerformanceCounter(Counter,SubCounter,Instance);

            else

                  this.Counter = new PerformanceCounter(Counter,SubCounter);

           

            if (Samples > 1) 

            {

                  for (int x=0; x < Samples; x++)

                  {

                        Value = Value + this.Counter.NextValue();

                        System.Threading.Thread.Sleep(5);

                  }

                  Value = Value / (float) Samples;

            }

            else

                  Value = this.Counter.NextValue();

 

//

//                      for (int x = 0; x < Samples; x++)

//                            Value =  Value + this.Counter.NextValue();

//                     

//                      Value = Value / Samples;

      }

      catch

      {

            Value =  -1.00F;

      }

 

      DataRow dr = this.dt.NewRow();

      if (Key == null)

            Key = SubCounter;

 

      dr["Counter"] = Key;

      dr["Value"] = Value;

      dt.Rows.Add(dr);

}

 

Security

Hmmm… Interesting side note. I had thought that Perf Counters and the like required Admin privileges, but it turns out that it works fine with the NETWORK SERVICE and no login at all.

 

So this was one of the reasons I mentioned yesterday that I use Impersonation on my admin pages so that I can log in on the server using my admin account and then be able to run. Anonymous access on this page will fail. Drat, I still can’t pull the Impersonation though because I need the ability to update Web.config for my Configuration Class that allows updateable configuration settings through the application.


The Voices of Reason


 

Andrea
May 27, 2005

# re: Displaying useful Configuration and Status information in an ASP.NET app

Very cool -- just added this to my monitoring toolkit. Thanks for posting the code.

Rick Strahl's Web Log
September 28, 2006

# Understanding ASP.NET Impersonation Security - Rick Strahl's Web Log

Understanding how ASP.NET's internal security works is important if your application needs to access resources on the local machine. Specifically it's important to know exactly which account your ASP.NET application is running under. This entry reviews different ways of how this account is affected by different versions of Windows, and ASP.NET configuration.

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024