1

Using Asp.net webforms I want to track down visitor info just like Google Analytics does. Of course, I can use Google Analytic for this purpose but I want to know how can I achieve the same thing with Asp.net 3.5 and SQL Server 2008.

I want to store IP, Country, URL Referrer of the visitor, Resolution on each page request except postback. I am expecting 50k+ visit everyday..

Main concern is I want to do it in a way that it should not block current request.

i.e In general it happens when we save data in to db, current request stops on particular SP calling statment and moves ahead when it finishes executing SP or tsql statement. I want to follow "Insert and Forget" approach. It should insert in background when I pass parameter to particular event or function.

I found below alternatives for this :
1. PageAsynchTask
2. BeginExecuteNonQuery
3. Jquery Post method and Webservice (But I am not confident about this, and wondering how should I go about it)

I hope I've mentioned my problem properly.

Can anybody tell me which one is better approach? Also let me know if you've any other ideas or better approach than the listed one. Your help will be really appreciated.

Nilesh Thakkar
  • 2,877
  • 1
  • 24
  • 43

4 Answers4

1

Talking about server side, if you're running on IIS, and you don't need absolute real time information, I recommend you use IIS logs.

There is nothing faster than this, as it's been optimized for performance since IIS 1.0

You can append your own information in these logs (HttpRequest.AppendToLog), they have a standard format, there is an API if you want to do custom things with it (but you can still use text parser if you prefer), and there are a lots of free tools, for example Microsoft Log Parser which can transfer data in a SQL database (among others).

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • Thanks Simon for responding. IIS Log file contains too much info than what required, I am not concerned if particular image is accessed number of times and it becomes difficult when you've data scattered in no of files. We can ofcourse parse log files using utility you mentioned or others available on net. It would be difficult to see activity within individual session and whether any visitor return. Response.AppendToLog appends detail in URI and if we anticipate other parameter there'll be length restriction. I am considering this as last option. Once again thanks for your invaluable input. – Nilesh Thakkar May 08 '11 at 12:43
1

Problems with any background thread in server side is each and every request is going to occupy two threads. One for serving the ASP.NET request and one for logging the stuff you want to log. So, you end up having scalability issues due to exhaustion of ASP.NET threads. And logging each and every request in database is a big no no.

Best is to just write to log files using some high performance logging library. Logging libraries are highly optimized for multi-threaded logging. They don't produce I/O calls on each and every call. Logs are stored in a memory buffer and flushed periodically. You should use EntLib or Log4net for logging.

You can use an HttpModule that intercepts each and every GET, POST and then inside the HttpModule you can check whether the Request.Url is an aspx or not. Then you can read Request.Headers["__ASYNCPOST"] and see if it's "true", which means it's an UpdatePanel async update. If all these conditions are true, you just log the request into a log file that stores the

You can get the client IP from:

HttpContext.Current.Request.UserHostAddress; 
or 
HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

To get the IP address of the machine and not the proxy use the following code

HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];

However you cannot get the country. You will have to log the IP in your log files and then process the log files using some console application or job which will resolve the country of the IP. You need to get some IP->Country database to do the job. I have used http://www.maxmind.com/app/geoip_country before.

For screen size, you will have to rely on some javascript. Use a javascript on each page that finds the size of the screen on the client side and stores in a cookie.

var screenW = 640, screenH = 480;
if (parseInt(navigator.appVersion)>3) {
 screenW = screen.width;
 screenH = screen.height;
}
else if (navigator.appName == "Netscape" 
    && parseInt(navigator.appVersion)==3
    && navigator.javaEnabled()
   ) 
{
 var jToolkit = java.awt.Toolkit.getDefaultToolkit();
 var jScreenSize = jToolkit.getScreenSize();
 screenW = jScreenSize.width;
 screenH = jScreenSize.height;
}

Once you store it in a cookie (I haven't shown that code), you can read the screen dimensions from the HttpModule by using Request.Cookies and then log it in the log file.

So, this gives you solution for logging IP, screensize, finding country from IP, and filtering UpdatePanel async postback from logging.

Does this give you a complete solution to the problem?

oazabir
  • 1,599
  • 9
  • 15
  • I've been using Entlib (DAAB + Exception handling + Logging) for it and for ip to country, I'll be using [this](http://www.ipinfodb.com). I was doubtful about the scenario you said regarding storing info in db, so what would be the alternative If I want to show different visitor details to admin in his dashboard? I think that wouldn't be possible with saving details in log files. Thanks for the resolution snippet that I'll be using along with HTTP Module as you said as I'll get required information on each request and module could be used in other projects as well. Really appreciate your help – Nilesh Thakkar May 08 '11 at 16:56
  • You should store the raw logs in log files. Then you can create some windows service or console application that processes the raw logs and then does the IP -> Country mapping and then stores the summary information in a database. I suggest not storing each and every visit in database. best to store some daily summary or country wise summary in database and show it from admin dashboard. – oazabir May 10 '11 at 19:54
0

The first approach is looking good. (And I recommend it.) But it has 2 downsides:

  1. Request will still block until task completes (or aborts on timeout).
  2. You'll have to register your Task on every page.

The second approach looks inconvenient and might lead to errors. (You have to watch for a situation when your page renders faster than your query is processed. I'm not sure what will happen if your query is not complete when your page object is destroyed and GC goes on finalize() spree... but nothing good, I assume. You can avoid it by waiting for IAsyncResult.IsCompleted after render, but that's inconvenient.)

The third method is plain wrong. You should initiate your logging on the server side while processing the request you're going to log. But you still can call a web service from the server side. (Or a win service).

Personally, I'd like to implement logging in BeginRequest to avoid code duplication, but you need IsPostback... Still there might be a workaround.

Dmitry
  • 3,069
  • 1
  • 17
  • 26
  • I appreciate your time taking to answer my question. – Nilesh Thakkar Apr 26 '11 at 18:55
  • I appreciate your time taking to answer my question. I was aware of second point for the first approach but first point could be the problem as you stated. Second approach I've never used but will check it out. I've dropped idea of using the third approach. As you said I can do it in BeginRequest but I guess session is not available in that event, so if I want to track down user specific info, it won't help me. I think I've to go with Application_PreRequestHandlerExecute as session will be available in that. I've gone through [this](http://tinyurl.com/l9xs4) article. – Nilesh Thakkar Apr 26 '11 at 19:10
  • I am still open for other ideas and better approach. – Nilesh Thakkar Apr 26 '11 at 19:13
0

Hii, you can fire an asynchronous request and don't wait for response. here i have some implemented code..

for that you need to create a web service to do your database operation or you can use it for your whole event handling.

from server side you have to call the web service asynchronously like this

Declare a Private Delegate

private delegate void ReEntryDelegate(long CaseID, string MessageText);

Now the method will contain web service calling like this

WebServiceTest.Notification service = new WebServiceTest.Notification();
IAsyncResult handle;
ReEntryDelegate objAscReEntry = new ReEntryDelegate(service.ReEntryNotifications);
handle = objAscReEntry.BeginInvoke(CaseID, MessageText, null, null);
break;

And the variable values will be passed by method here (CaseID,MessageText)

Hope this is clear to you

All the Best

Sandip
  • 981
  • 1
  • 6
  • 22