Howto Create a Hit Counter Using the Global.asax File in ASP.NET 1.x

If you have a live Web site on the World Wide Web, you may be interested in how many people are visiting your site. You can of course analyze the log files of your Web server but that information is usually difficult to read. The log files contain information for each and every request a visitor has made to your site, including resources like images, Flash movies and so on. This makes it near impossible to extract information about individual users. It would be a lot easier if you could count the number of individual users that have visited you since you started your site. It would also be useful if you could see the number of users that are currently browsing your site.

This article will show you how to accomplish these two tasks by storing the hit counters in static variables using code in the Global.asax file. The disadvantage of this method is that this information is lost when you restart the Web server. Two other articles on this site demonstrate how to store this information in a text file and in a database, so the value for the counter will be preserved when you restart your Web server.

There is also a Classic ASP version of this article available

Prerequisites

The code in this article uses Sessions in ASP.NET, so you'll need to have them enabled on your server, by configuring the <sessionState> element in the Web.config file. Refer to the References section at the end of this article for more details.
You'll also need to have access to a file called Global.asax in the root of your site. If you run your own Web server, this is not a problem; you can simply create the file yourself. If you are using an ISP, you'll need to check with them if they support the use of the Global.asax file as, unfortunately, not all ISPs allow this.
This article also assumes you're using Visual Studio .NET 2002 or 2003, as it shows code and ASPX pages using the Code Behind model. Don't worry if you don't have Visual Studio .NET; it should be relatively easy to use the code in your own Code Editor like Macromedia Dreamweaver MX or Notepad.

Counting Users

One of the easiest ways to count individual users is in the Session_Start event that you can define in the Global.asax file. This event is fired whenever a user requests the first page in your Web site. This way, you have the ability to count each unique visitor only once during their visit. As long as the Session remains active on the server, the user won't be counted again. After the Session has timed out (it will automatically time out after a certain interval when no new pages have been requested) or has been explicitly ended, a request to a page will create a new Session, and the user will be counted again.

To keep track of the total number of users that have visited your site since you started the Web server, you can increase a counter for each request a user makes. Let's call this counter TotalNumberOfUsers. You can store that counter in a static variable in the Global class. This Global class is defined in the file Global.asax.cs, the Code Behind file for Global.asax. By creating static variables, you can be sure there is always just one instance of your hit counter variable present. Because the class defined in the Global.asax file is accessible throughout the entire application, you can retrieve and display the value for the counter on other pages in your site.

You can also create a second counter, called CurrentNumberOfUsers for example, that counts the number of active Sessions on your server. Just as with TotalNumberOfUsers, you increase the value of this counter whenever a new Session is started. However, you should decrease its value again when the Session ends. so you can keep track of the number of users that are currently visiting your site.

Let's take a look at how you can accomplish this:

You should start by making sure you have a file called Global.asax (note that the extension is different from ordinary aspx pages) in the root of your Web site. Usually, when you create a new Visual Studio ASP.NET Web Application, the Global.asax file is already there, and the skeleton for important events like Session_Start and Session_End are already present.
If you don't have the file, open the Visual Studio .NET Solution Explorer (Ctrl+Alt+L), right-click your Web project and choose Add | Add New Item... from the context menu. Scroll down the list with Web Project Items until you see Global Application Class. Alternatively, expand Web Project Items and then click Utility to limit the list of Web items. Select the Global Application Class and click Open to add the Global.asax file to your Web site project.

The page will open in Design View so you'll need to click the link "click here to switch to code view" to view the Code Behind file for the Global.asax file. You'll see some default using statements, followed by the definition for the Global class. You'll expand this class by adding a few private variables for the two hit counters. These private variables will then be made accessible through public properties. Using properties instead of public fields helps keeping your code cleaner and more stable. Calling code is not allowed to just arbitrarily change the field's value; it has to change the value through a public Set method. In this example, you'll make the code even a bit more safe by removing the Set method altogether. This makes it impossible for calling code to change the value of the counter; all it can do is read its value.

Modifying Global.asax


  1. Locate the code that starts with public class Global : System.Web.HttpApplication and add the following shaded lines of code:
namespace HitCounters
{
  /// <summary>
  /// Summary description for Global.
  /// </summary>
  public class Global : System.Web.HttpApplication
  {
    private static int totalNumberOfUsers = 0;
    private static int currentNumberOfUsers = 0;

    /// <summary>
    /// Required designer variable.
Whenever the Web application starts, the Global class is constructed and the two hit counters will be initialized to 0.

  1. The next step is to add code to the Session_Start event. This event will fire once for each user when they request the first page in your Web application, so this place is perfect for your hit counter. Inside this event, the values of the two hit counters are increased; one for the total number of users and one for the current number of users.
    Locate the skeleton for the Session_Start event and add the following code:
protected void Session_Start(Object sender, EventArgs e)
{
  totalNumberOfUsers += 1;
  currentNumberOfUsers += 1;
}
  1. Just as with the Session_Start event, you'll need to write some code for the Session_End event. However, instead of increasing the counters, you should decrease the counter for the current number of users only. This means that whenever a user Session times out (usually 20 minutes after they requested their last page), the counter will be decreased, so it accurately holds the number of current users on your site. You should leave the counter for the total number of users untouched.
    Locate the Session_End event, and add this code:
protected void Session_End(Object sender, EventArgs e)
{
  currentNumberOfUsers -= 1;
}

Making Your Counters Accessible by Other Pages

Since the hit counters are stored in the Global class, you need some way to get them out of there, so you can display them on a management page for example. The easiest way to do this, is to create two public properties. Because the Global class is in many respects just an ordinary class, it is easy to add public properties to it.

To add the properties, locate the skeleton for the Application_End event, and add the following code right below it:

protected void Application_End(Object sender, EventArgs e)
{

}

public static int TotalNumberOfUsers
{
  get
  {
    return totalNumberOfUsers;
  }
}

public static int CurrentNumberOfUsers
{
  get
  {
    return currentNumberOfUsers;
  }
}
With these two read-only properties in place, your calling code is now able to access the values of your hit counters. For example, to retrieve the number of users browsing your site right now, you can use this code: HitCounters.Global.CurrentNumberOfUsers where HitCounters is the default namespace for your Web application as defined on the Property Pages for the Web project in Visual Studio .NET.

Testing it Out

To test out your hit counters, create a new Web form and call it HitCounter.aspx. You can save the form anywhere in your site. In Design View, add two labels to the page and call them lblTotalNumberOfUsers and lblCurrentNumberOfUsers respectively. Add some descriptive text before the labels, so it's easy to see what value each label will display. You should end up with something similar to this in Code View:

<body>
  <form id="frmHitCounter" method="post" runat="server">
    Total number of users since the Web server started:
    <asp:label id="lblTotalNumberOfUsers" runat="server"></asp:label><br />
    Current number of users browsing the site:
    <asp:label id="lblCurrentNumberOfUsers" runat="server"></asp:label><br />
  </form>
</body>
Press F7 to view the Code Behind for the hit counter page, and add the following code to the Page_Load event:

private void Page_Load(object sender, System.EventArgs e)
{
  int currentNumberOfUsers = HitCounters.Global.CurrentNumberOfUsers;
  int totalNumberOfUsers = HitCounters.Global.TotalNumberOfUsers;
  lblCurrentNumberOfUsers.Text = currentNumberOfUsers.ToString();
  lblTotalNumberOfUsers.Text = totalNumberOfUsers.ToString();
}
The first two lines of code retrieve the total number of visitors and the current number of visitors from the Global class. The next two lines simply display the values for the counters on the appropriate labels on the page.

To test it out, save the page and view it in your browser. You'll see there is one current user. Also, note that the total number of users is 1. Open another browser (don't use Ctrl+N, but start a fresh instance or use an entirely different brand of browser) and open the counter page. You'll see there are two current users, and two users in total. Wait until both the Sessions have timed out (the default timeout defined in Web.config is 20 minutes) and open the hit counter page again. You'll see there is one current user, but the total number of users has been maintained and should be 3.

Summary

This article demonstrated how to create a hit counter that keeps track of the current and total number of users to your site. It stores these counters in static variables in the Global class so they are available in each page in your Web site. A big disadvantage of storing these variables in static variables is that their values get lost when the Web server is restarted.

Two other articles on this site will demonstrate how you can save the counter value for the total number of users in a text file or in a database. By saving the counter to a text file or database, its value can be maintained, even when you restart or reboot the Web server.

Related Articles

References

Download Files


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Friday, April 29, 2005 6:51:53 AM rohollah said:
hello!
Thank you for your very usefull article.
On Tuesday, December 27, 2005 12:08:33 PM nalaka said:
how can i validate to users. I mean to maintain user(PC) level hit counters. In other words increment the counter only once to one user (PC)
On Wednesday, December 28, 2005 3:43:34 PM Imar Spaanjaars said:
Hi nalaka,

There really isn't a reliable way to do this, if your site doesn't require your user to authenticate.

What you could do is store a cookie that keeps track of the fact the user logged in. Next time you want to count, don't increment the counter when the cookie is already there.

Other methods include keeping track of the user's (network) name or IP address. However, these methods will only succeed in closed environments, like an Intranet.

HtH,

Imar
On Saturday, May 06, 2006 5:44:35 AM Molina said:
This example is very good, but dosn't work for me. The code increment the variable but the Session_End method of the global.asax is never called.
On Saturday, May 06, 2006 7:53:12 AM Imar Spaanjaars said:
Hi Molina,

Are you using SQL Server as the data store for sessions in your ASPX pages? That is, did you configure the web.config file and changed the mode attribute of the sessionState element to SQLServer, like this:

sessionState mode="SQLServer"

In that case, SessionOnEnd will never fire. This is by design....

Imar
On Tuesday, November 07, 2006 9:15:14 AM thuongmaitudo@yahoo.com.vn said:
ASP.NET Web Page  Show current user count on web site
On Tuesday, November 07, 2006 6:11:36 PM Imar Spaanjaars said:
Hi thuongmaitudo,

Heuh? I am not sure what you mean. Can you elaborate a bit please?

Imar
On Monday, December 11, 2006 1:57:57 PM Srinivas said:
thank u lot ' Create a Hit Counter Using the Global.asax File in ASP.NET ' its too usefull for me.Its very very simple and easyiest coding for Hit Counter . Thank u once again
On Wednesday, January 10, 2007 12:18:08 PM Mohit Raghav said:
Gud article, but u have not taken that case when user closes his browser by clicking on X button on top-right corner.
Inn that case Session_End() does not fire immediately.

What do u say for this ???

On Wednesday, January 10, 2007 8:44:52 PM Imar Spaanjaars said:
Hi Mohit,

Take a look here for my take on Session_OnEnd when the user closes the browser:

http://p2p.wrox.com/topic.asp?TOPIC_ID=1774

Cheers,

Imar
On Wednesday, January 24, 2007 12:32:33 PM dinesh said:
c:\inetpub\wwwroot\WebApplication1\HitCounter.aspx.cs(24): 'WebApplication1.HitCounter' does not contain a definition for 'Global'


int currentNumberOfUsers = HitCounter.Global.CurrentNumberOfUsers;


for this line in the code ... the abve error is coming...
pls help what could be the wrong..
On Wednesday, January 24, 2007 7:19:37 PM Imar Spaanjaars said:
Hi dinesh,

Sounds like there's something wrong with your setup. Are you sure your Global.asax is OK? Is it in the same namespace as the rest of the application?

Imar
On Thursday, January 25, 2007 11:34:55 AM dinesh said:
yeh imar..
other asp.net applications are working but here the error coming like that.
On Thursday, January 25, 2007 11:59:17 AM dinesh said:
Yess
imar.. its working.. i havent changed the name of my project to HitCounters.
so i did changed and its working..
thanks.. this was a nice article...
On Thursday, March 01, 2007 11:57:51 AM Joe said:
in VB, where do i find public class Global : System.Web.HttpApplication from global.asax?
On Thursday, March 01, 2007 12:15:30 PM Imar Spaanjaars said:
Hi Joe,

In VS 2003, when you add a Global Application Class through File | Add New Item, you get a file called Global.asax with a code behind file that contains the Global class.....

Isn't this what you are looking for?

Imar
On Monday, June 18, 2007 12:49:40 PM Sanjeev Kumar said:
I can not being able in accessing public properties in globle.aspx file in oher .aspx files. I am using asp.net 2.0
On Monday, June 18, 2007 3:35:41 PM Imar Spaanjaars said:
Hi there,

In a standard VS 2005 project, the Global class works a little differently. However, it's easy to fix. Take a look here:

http://rossnelson.blogspot.com/2005/11/fixing-globalasax-in-aspnet-20.html

Basically, you need to move the code to a separate Global file, add an Inherits attribute to that class in Global.asax and move the code from the old Global.asax file into the new Global class.

HtH,

Imar
On Monday, July 23, 2007 5:39:09 AM soumya dasgupta said:
Hi,
i am workin on an application where we use static variables for storing user info. now when 2 users or more are logged in the same machine, the application is not showing the other person's name and it is showing only one username and that same username is dispalyed for other users too.
Is this because we are using global[static] variables for the user info?
please help me out with a solution to this issue
On Monday, July 23, 2007 6:05:49 AM Imar Spaanjaars said:
Hi soumya,

Static variables apply to the *entire* class they are used in; not to just an instance of that class.

So you cannot use static varaibles for user related data. Change it for one user, and it affects all other users as you found out.

Look at other means to store user data like ASP.NET's Profile feature or Session state.

Cheers,

Imar
On Tuesday, August 21, 2007 6:04:07 AM Lintu said:
hi,this article very helpful.It is working.
thanks a lot..
On Wednesday, August 22, 2007 9:39:59 AM Lintu said:
hi
it is working in local system.but not working in server(i mean upload)
Session_start not working
pls help me
On Wednesday, August 22, 2007 10:05:32 AM Imar Spaanjaars said:
Hi Lintu,

Hard for me to say without seeing or knowing anything about your setup. May I suggest you post this on a forum like http://p2p.wrox.com?

If you do post there, be sure to provide lots of information that may help in finding a solution (version of .NET, server OS, settings and so on).

Cheers,

Imar
On Thursday, November 15, 2007 10:52:23 PM Jeff said:
Is this thread safe?  I am just wondering how different this is then just storing the values in the application object.  If you don't use the Application.Lock() and Application.Unlock() methods to serialize the access then you can end up with incorrect values.
On Thursday, November 15, 2007 11:03:29 PM Imar Spaanjaars said:
Hi Jeff,

Yes, you're correct. You can use lock statements, or simply call Application.Lock and Application.UnLock to prevent the problem.

HtH,

Imar
On Friday, November 16, 2007 5:58:48 AM Chetan said:
Could you please update the code with Lock?
On Friday, November 16, 2007 8:22:17 AM Imar Spaanjaars said:
Hi Chetan,

Not right now as I don't have the time to update the article and it's source. However, it's simple to do. Just call Application.Lock before you change the variables and call Application.Unlock again when you're done.

Imar
On Thursday, May 08, 2008 3:06:56 AM Fraser said:
I suspect this isn't thread-safe...

Even incrementing an int isn't atomic, and therefore not thread-safe...

You probably need to use:

lock (currentNumberOfUsers)
{
    // Update your variables
}
On Thursday, May 08, 2008 5:42:10 AM Imar Spaanjaars said:
Hi Fraser,

Application.Lock will do the trick as well in an ASP.NET application as discussed in earlier comments.....

Cheers,

Imar
On Thursday, May 08, 2008 5:59:34 AM Fraser said:
Well, yes and no...

Application.Lock() will lock access to the Application (HttpApplicationState) object, which will have the effect of making your code a critical section.  But it will also lock any other thread out of the Application state, which is an unnecessary side-effect.  Since the article is about using static variables (which is a good idea in an ASP.NET application; I suspect the availability of the Application object is more about "backwards-compatibility" fo ASP developers), locking on one of the variables involved is more appropriate.
On Tuesday, September 09, 2008 11:47:49 AM Fredrik said:
The Session_End event will fire (when the user closes his browser) but only after the timeout has elapsed (by default, this is 20 minutes).
There is no way fpor the server to know that your browser is closed, unless you use some sort of "unload"-javascript or postback when closing the page.

On Tuesday, September 09, 2008 12:28:25 PM Imar Spaanjaars said:
Hi Fredrik,

The closed browser doesn't have anything to do with this; it will timeout even if you leave the browser open, and it won't fire directly after you close the browser.

But what exactly is your post about? Are you trying to add info? Correct something? Answer a post from someone else?

Imar
On Friday, September 12, 2008 11:52:38 AM Venkat said:
Well. it was very useful. thanks.
If a user has opened a session, the same user should not  open another session.. How to handle.. whether it has be  handled Application state level..
Any article / sample on this.
On Friday, September 12, 2008 12:32:00 PM Imar Spaanjaars said:
Hi Venkat,

This is not so easy to implement. It all depends on what you mean by a "session", e.g. how you define a unique user.

If you force a user to login, you could keep track of the logged in user in the database.
Otherwise you need to do messy things with IP addresses which is far from perfect.

Cheers,

IMar
On Thursday, April 29, 2010 12:14:54 PM Abhirup Biswas said:
toooooooooooooooo good.... excellent
am reallt satisfied reading this article
On Friday, July 02, 2010 4:43:52 AM camilo ospina said:
hello, how can i get the connected users value trought sessions variable.

example:

USers actually connected:
camilopro
anonimus
ppeepe

this dinamically.

thanks in advance.
On Friday, July 02, 2010 7:03:25 AM Imar Spaanjaars said:
Hi there,

It's pretty much the same, but rather than keeping track of a counter, you add and remove user names from some array or other collection stored in a static variable or application state.

Alternatively you could store the data in a database.

Cheers,

Imar
On Saturday, March 22, 2014 11:30:42 PM prince said:
pls how do i save the hitcounter values to a .txt file on my  remote server
On Sunday, October 18, 2015 10:35:48 PM Ajodo Godsown said:
how will i do it in asp.net 4.5 in visual studio 2012, when there is no  public class Global : System.Web.HttpApplication, and my global.asax page is like this:
[%@ Application Language="C#" %]
[%@ Import Namespace="WebSite3" %]
[%@ Import Namespace="System.Web.Optimization" %]


  
[script runat="server"]

    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup
        BundleConfig.RegisterBundles(BundleTable.Bundles);
        AuthConfig.RegisterOpenAuth();
}

void Session_Start(object sender, EventArgs e)
{

// Code that runs when a new session is started
}

    void Application_End(object sender, EventArgs e)
    {
        //  Code that runs on application shutdown

    }

    void Application_Error(object sender, EventArgs e)
    {
        // Code that runs when an unhandled error occurs

    }

[/script]
what will i do please?
On Tuesday, October 20, 2015 4:39:59 PM Imar Spaanjaars said:
You should be able to add it to that file, just outside of any other methods.

But again: this is old stuff, so you may need to rethink the whole implementation.

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.