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

TraceNet: Trace method level performance for DLLs in ASP.NET applications without writing any performance tracing code!

Rate me:
Please Sign up or sign in to vote.
4.82/5 (29 votes)
2 Dec 2010CPOL19 min read 81.9K   662   75   30
A component that allows you to view tracing information for methods written within classes in managed DLLs, and for this, you don't need to write any tracing code.

Introduction

How would it be if you could get a nice simple performance tracing output for a method call in an ASP.NET page as follows?

TracingOutput.png

I know, you will love it. Why? Because of the following reasons:

The built-in tracing engine in ASP.NET gives you some tracing information about the methods and events in the code-behind of pages and user controls, but it doesn't tell you the full story. Your applications are typically multi-layered, and you have different layers performing different functionality. Each different layer is usually implemented in a Class Library which outputs an assembly (DLL) to be referenced and used by the main application to carry out its work, and ASP.NET tracing cannot trace the methods written within these class libraries (DLLs).

Typically, the business logic methods are called from methods and events written in code-behind files, and data access/operation methods are called from within business logic methods. So, when you want your application to perform efficiently, you would want to make sure that it executes all methods in the code-behind, business logic layer, and data access layer as efficiently as possible. To make sure of this, you obviously need to know how much execution time is currently being taken by all these methods, so that you can identify which methods are performing inefficiently, and after optimization, you can identify whether performance has really improved, and if yes, how much the performance has gained.

Well, obvious story. Everyone knows that ASP.NET tracing gives you the execution time each code-behind method takes. Unfortunately, ASP.NET tracing does not tell you how much execution time is taken by the methods within the methods in those different layers. But, is it really hard to measure how much execution time these methods take? Not really. Let's look at the following example:

C#
public void AMethod()
{
    //Capture the starting time
    DateTime startTime = DateTime.Now;

    //do some operations
    //Capture the ending time
    DateTime endTime = DateTime.Now;

    //Find the time difference, this is the execution time of the method
    TimeSpan executionTime = (endTime - startTime).TotalMilliSeconds;
}

Basically, this method measures the execution time just by calculating the time difference between the start time and the end time recorded before and after the code execution within the method. The logic is quite simple, and it is a matter of just three lines of code.

The problem is, it is not easy to write these three lines of code in each and every method to capture the execution time of that method. To be true, it is kind of annoying to write these codes in each method of the application. Also, writing performance tracing code in each method does not give you the information about the call hierarchy and the nesting structure of method executions. Besides, as an application developer, while you write code, you want to be focused on solving the core business problem of your target domain and getting your work done, rather than worrying on cross-cutting issues like performance tracing, logging, exception handling, etc.

Wouldn't it be really nice if we could get method level execution times without writing performance tracing code? Also, how nice would it be if we could get the call hierarchy and the nesting structure of methods, along with the parameter list and information about any occurrence of exceptions within DLL methods, and for that, what if you don't have to write a single line of code? I know, you will simply love it.

This article is all about implementation of a component (a DLL) that lets you get method level performance tracing information (along with a nested call stack with proper indenting, parameter list, and exception information) simply by adding a reference to it in your class libraries and doing a little more changes (like using the object factory of the DLL to instantiate objects, etc).

I named the component TraceNet :)

How to use TraceNet?

TraceNet is a DLL (a Class Library in Visual Studio) which you can use to trace method level performance for your ASP.NET applications. Following are the things you need to do to use the tracing component and get the method level tracing output:

  • Add a reference to TraceNet.dll in your Class Library and application projects, and instantiate the objects (for which you want to trace method execution performance) of your application by using the object factory InterceptionObjectFactory<T> which is available within TraceNet. For example:
  • C#
    //Instead of instantiating object as follows:
    StudentDAO dao = new StudentDAO(),
    
    //instantiate object as follows (without any constructor argument)
    StudentDAO dao = InterceptionObjectFactory<StudentDAO>.GetObject();
  • Make sure the methods which you want to trace are virtual.
  • Image 2

  • Configure the Web.config with the following HttpHandler and HttpModule (along with making sure that the existing HttpHandler and HttpModule configurations are there) which are used by the TraceNet component:
  • XML
    <httpHandlers>
        <add verb="*" path="TraceNet.axd" 
             type="TraceNet.TraceNetHandler,TraceNet"/>
    </httpHandlers>
    
    <httpModules>
        <add name="TraceNetModule" 
             type="TraceNet.TraceNetModule,TraceNet" />
    </httpModules>

    The HttpHandler (TraceNetHandler) is executed for writing trace output when user hits an ASPX page with the request parameter Trace=true, and the HttpModule (TraceNetModule) is executed for writing trace output when user hits Trace.axd to view the tracing output (more on viewing tracing output in the following sections).

  • While executing an ASPX page, add a query string parameter Trace=true to see the tracing output. For example: http://www.mysite.com/default.aspx?Trace=true.

If you are developing a new application from scratch, setting up the application to use TraceNet would be really easy. But, if you have an existing application in which you want to use TraceNet, you will need to do a bit of work to implement the mentioned changes. Mainly, the objects (which you would like to trace) are to be instantiated using the object factory of TraceNet, instead of instantiating them directly, and the methods are to be marked as virtual (along with other minor things to do). The good thing is, none of these changes are hard, and if you already have some good coding practices implemented (there may already be some object factory for producing objects in your application), you will only need to change a few lines of codes within the factory to use the object factory of TraceNet. Adding the virtual keyword for existing methods could be done quickly by a smart use of the "Find..Replace" tool of Visual Studio.

Alternative way of seeing trace output

The methods within the class libraries (DLLs) are not only being called from within the code-behind classes of *.aspx pages, but they can also get called from within the Web Service methods (*.asmx), or they can even be invoked from an AJAX request from the browser. It is not possible to use the query string parameter "Trace=true" to get performance trace output in these situations. So, there has to be an alternative way of seeing the method level traces when they are executed from the non-ASPX pages.

To facilitate this, there is a separate URL that could be executed to see the method level trace outputs. After hitting any ASPX page, or performing any postback activity, or hitting any Web Service method, or performing an AJAX operation, you can hit the following URL to see the trace output: http://www.yoursite.com/TraceNet.axd (change www.yoursite.com to the appropriate site URL).

However, to enable this tracing, you need to configure a parameter in web.config, as follows:

XML
<add key="Tracing" value="true"/> 

As long as the above configuration value is set to "true", tracing will be enabled for this application, and hence methods will be intercepted even if the request URL doesn't contain any query string "Trace=true". So, once tracing is done, make sure to set the value to "false" for this configuration parameter in web.config.

Please note that once this URL is executed and trace output is shown in the browser, tracing information is cleared from the system. So, the immediate next hit at the above URL will return an empty page output, and to see the tracing output again by executing this URL, the corresponding page or Web Service method has to be executed first.

How to disable tracing

The tracing mechanism could be disabled any time so that the tracing component never intercepts any method invocations (which is expensive). Just set the following configuration value to "false" and that's it!

XML
<add key="Tracing" value="false"/>

The sample application

I've attached a sample application which uses the TraceNet component to trace method level performance and other cross-cutting issues for the DLLs it uses. Download the sample ASP.NET application (TraceNetDemo.zip), open the solution in Visual Studio, and have a look at the solution to learn how to use the component for an ASP.NET application.

The solution has the following structure in Visual Studio:

TracenetSolutionExplorer.png

The TracenetDemo solution has a web site, which uses two class libraries "App.Business" and "App.Data" for implementing business logic and database operations, respectively. It uses the TraceNet class library for implementing tracing for this application.

The application isn't very fancy, and it has some stupid methods which also have some stupid logic to perform some actions (see Default.aspx.cs) within the DLLs. Sometimes an exception is thrown from within a method intentionally, and you may even find a Delete() method being called within a Get() method! Basically, my intention is not to implement some business logic in the proper and correct way. Rather, I want to demonstrate the ability of the tracing component, and for that, I invoke some business logic and data access methods in a random basis which doesn't comply with any logical order. The bottom line is, the method implementation logic is not important, and the "demonstration" of the tracing capability is the main point of interest.

Viewing trace output for ASPX page hit

To take a look at TraceNet's demo, configure the web application in IIS, or open the solution in Visual Studio, and run/debug the application using the built-in development server (IIS) to browse the following page: http://localhost:5957/Web/Default.aspx?Trace=true.

[Caution: You may need to change the port number (the port number 5759 to the appropriate one in the following URLs) in the URLs while running the application using Visual Studio Development Server.]

Adding Trace=true with the request URL will execute the page and display tracing output on the page, and the output will be shown below the regular page output (the label "This is regular output from page" and the buttons are the regular page output here):

TraceOutputFromPage.png

Alternatively, browse http://localhost:5957/Web/Default.aspx first and then hit http://localhost:5957/Web/TraceNet.axd to view tracing information (of course, tracing has to be enabled in web.config to be able to do this).

Viewing trace output for postback action

To view tracing output of a postback action, hit the button "Postback Action" in Default.aspx. The postback action will be executed, and you will see the following tracing output now:

PostBack_action.png

Viewing trace output for AJAX action

To trace the performance of an AJAX action, browse Default.aspx. You will see the following output:

AjaxButton.png

Now, hit the "Ajax action" button. The AJAX action will be executed on the server side and the label will be changed as follows: "This is regular page output for ajax action".

AjaxActionExecuted.png

And hitting the following URL will show the trace output of the AJAX action executed on the server: http://localhost:5957/Web/TraceNet.axd.

AjaxActionTrace.png

Viewing trace output for Web Service method execution

To trace the performance of a web method, browse the following web method by hitting the following URL in browser: http://localhost:5957/Web/WebService.asmx?op=HelloWorld.

And click the "Invoke" button. The method will be invoked and the following output will be shown in the browser: "<string>Hello World</string>".

Hit http://localhost:5957/Web/TraceNet.axd again to view the performance trace output of the web method:

WebMethodTracing.png

How TraceNet works

Very important question.

For getting method level tracing output within class libraries (DLLs), we do not need to write any performance tracing code in the methods. So, the tracing component must be intercepting method calls when these methods are invoked by the application. Yes, you guessed it right, AOP (Aspect Oriented Programming) is the key here!

There is one important issue with AOP usage. The issue is, the "Method interception strategy".

Strategy1: Intercept method calls using the framework's built-in way

If any class inherits the System.ContextBoundObject object, each and every method invocation within an object of that class can be intercepted via a method, with the help of a bit more code to be implemented. But, this strategy has two important downsides:

  • The application developer has to "know" and write the method interception code, and this is not good. I would like to see the "Principle of least knowledge" being implemented here so that the application developer doesn't have to write a lot of code about method interception and performance tracing, because he/she has lots of things to be worried about regarding the business logic implementation of the application.
  • The application classes will get tightly bound with the ContextBoundObject because they will have to inherit this class. This is a heavyweight class, and once a class inherits it, all of the method invocations are intercepted at runtime, which is expensive. Problem is, in the production environment, you would want to turn off the tracing mode in the normal situation, and if any problem occurs, you would want to turn it on to trace the application. Use of ContextBoundObject does not let you do this.

You could, however, use some method level attributes to declare themselves as "not interested to be intercepted", but again, this requires the application developer to know about the method interception issue, and this violates the "Principle of least knowledge".

Strategy 2: Inject (emit) code within the DLLs using Reflection

This sounds a smart way. It might be possible to inject some code to capture tracing information within the methods of classes by using System.Reflection, but this sounds too much a thing to do with the outcome we would want to have. Also, not all might be happy with this strategy because they wouldn't take the risk of letting someone inject some alien code into their favorite DLLs.

Strategy 3: Proxy the object and override the interested methods

A simpler way of implementing method interception could be as follows:

  • Constitute a proxy object from a class that is built at Runtime by extending the target class, override the methods (if they allow themselves to be overridden) by declaring the "virtual" keyword.
  • Expose a common single method (mimics an interception method) and call the actual target method within it, along with writing some tracing code within this method.
  • Call the interception method in place of the target method execution.

This option looks better to me, because of the following reasons:

  • The method interception could be turned on or off any time. We could have a factory class which would provided us the objects of classes which we would like to intercept. Based on configuration, the factory would either build a proxy object of the target class (when tracing is turned on) which intercepts the methods (marked as virtual), or would instantiate and return the object of the original class (when tracing is turned off). A configuration parameter could be used so that the factory can decide which object to instantiate and return based on the value.
  • The implementation is easier, because there are some existing frameworks which provide the mechanism of building proxy objects and intercepting methods (marked as virtual).
  • The application developer does not have to know a lot about method interception, and he/she doesn't have to write performance tracing code. Whatever methods within the class libraries (DLLs) for which we want performance trace information, we are required to declare the methods as virtual, and instead of instantiating the objects directly, we need to instantiate them using the object factory; that's it.

C# does not let you override methods automatically, which Java does. So, the virtual keyword has to be used for methods which are to be overridden and intercepted, to see the performance tracing output. This is a minimal thing to do, and could be done easily by using the "Find.. Replace" feature in Visual Studio.

Detailed implementation

There are two core functional modules in the tracing component:

  • Intercepting and gathering trace information.
  • Displaying trace information in a hierarchical structure, according to the call stack.

Let's look in to the details of how the above two modules are implemented.

Intercepting and gathering trace information

As has been said already, following are the things that are to be performed for intercepting method calls using our prefered strategy 3:

  • Constitute a proxy object from a class that is built at Runtime by extending the target class. Override the methods (if they allow themselves to be overridden) by declaring the virtual keyword.
  • Expose a common single method (mimics an interception method), and call the actual target method within it, along with writing some tracing code.
  • Call the interception method in place of the target method execution.

I could try to develop an engine that would build the proxy object on the fly and would do all the magical stuff of method interception for me. Problem is, I am a lazy guy, and I didn't want to write it myself :). So, I opted to look for some existing frameworks or components, and it didn't take too long for me to find that the "Castle" project (http://www.castleproject.org/) has a pretty nice and neat implementation of exactly what I needed.

The object factory

Following is the object factory implementation which instantiates the proxy object using the ProxyGenerator class in the Castle library and returns the object. It has two overloaded versions of the same generic method GetObject() which instantiates the proxy object of the target type T: one with the constructor arguments, and another without any constructor arguments. The CreateClassProxy() method is used to create the proxy object, and this method has to be supplied with the object of the class which contains the Intercept() method.

C#
/// <summary>
/// Object factory for creating and returning
/// proxy object for intercepting methodsS
/// </summary>
/// <typeparam name="T"></typeparam>
public class InterceptionObjectFactory<T> where T : class
{
   //The ProxyGenerator class is from the Castle library
   static ProxyGenerator generator = new ProxyGenerator();

   /// <summary>
   /// Instantiate and returns proxy object of type T,
   /// without any constructor argument
   /// </summary>
   /// <returns></returns>
   public static T GetObject()
   {
       T t;

       if (PerformanceTracer.TraceEnabled)
       {
           //Create proxy object if tracing is enabled
           //either in configuration or in
           //Request parameter.
           t = generator.CreateClassProxy<T>(new MethodInterceptor());
       }
       else
       {
           //Do not create proxy object as tracing is not enabled.
           //Rather, instantiate regular object of type T
           //without constructor argument and return
           t = Activator.CreateInstance<T>();
       }
       return t;
   }

   /// <summary>
   /// Instantiate and returns proxy object of type T,
   /// with the constructor arguments
   /// </summary>
   /// <param name="ConstructorArgs"></param>
   /// <returns></returns>
   public static T GetObject(object []ConstructorArgs)
   {
       object obj;

       if (PerformanceTracer.TraceEnabled)
       {
           //Create proxy object if tracing is enabled either
           //in configuration or in Request arameter
           obj = generator.CreateClassProxy(typeof(T), 
                           ConstructorArgs, new MethodInterceptor());
       }
       else
       {
           //Do not create proxy object as tracing
           //is not enabled. Rather, instantiate regular
           //object of type T with constructor arguments and return
           obj = Activator.CreateInstance(typeof(T), ConstructorArgs);
       }
       return (T)obj;
   }
}

How to instantiate objects to intercept the methods it contains

To intercept methods and gather performance trace information for methods within a class, one of the most important things is the object of the class has to be instantiated through the object factory (InterceptionObjectFactory) of TraceNet. Following is how the objects are to be instantiated, using this factory:

C#
//DO NOT instantiate object as follows (Objects which are to be intercepted):
StudentDAO dao = new StudentDAO(),

//Instead, instantiate object as follows (without any constructor argument)
StudentDAO dao = InterceptionObjectFactory<StudentDAO>.GetObject();

//and,

//DO NOT instantiate object as follows (Objects which are to be intercepted):
StudentDAO dao = new StudentDAO(1,"Shubho"),

//Instead, instantiate object as follows (With constructor argument)
dao = InterceptionObjectFactory<StudentDAO>.GetObject(new object[] { 1, "Shubho" });

Method interception and gathering performance data

In the InterceptionObjectFactory<T> class, the CreateClassProxy() method is called to create the proxy object, and that object has to be supplied with the object which implements the IInterceptor interface. The interface contains a single Intercept() method, which has to be implemented by the interceptor class. Following is the Intercept() method implementation within the MethodInterceptor class:

C#
/// <summary>
/// The Interceptor class
/// </summary>
public class MethodInterceptor : IInterceptor
{
   /// <summary>
   /// Method that is being invoked whenever
   /// the target method is called in the application
   /// </summary>
   /// <param name="invocation"></param>
   public void Intercept(IInvocation invocation)
   {

       string methodName = string.Format("{0}.{1}()", 
              invocation.TargetType.Name, invocation.Method.Name);

       //Calculate the indenting of the current method call
       bool isWebMethod = false;
       int indent = PerformanceTracer.CalculateIndent(ref isWebMethod);

       ///Get the method invocation serial
       int Serial = PerformanceTracer.GetSerial(isWebMethod);

       //Date time value before executing the target method
       DateTime before = DateTime.Now;
       Exception exp = null;

       try
       {
           //Execute the target method
           invocation.Proceed();
       }
       catch (Exception ex)
       {
           //Catch if any exception occurs while executing the target method
           exp = ex;
       }

       //Date time value before executing the target method
       DateTime after = DateTime.Now;
       TimeSpan t = after - before;

       //Obtain parameter information for the method invocation
       Hashtable htParams = Getparameters(invocation);

       //Create a TraceEntry object, which is used
       //to render tracing output in the browser
       TraceEntry entry = new TraceEntry(indent, methodName, 
          t.TotalMilliseconds, htParams, Serial, 
          invocation.ReturnValue,isWebMethod);

       if (exp != null)
       {
            entry.Exception = exp;
       }

       //Store the TraceEntry object
       PerformanceTracer.StoreEntry(entry);
    }
...
}

The Intercept method does the following things:

  • Calculates the indenting value based on the position of the corresponding method in the call stack.
  • Calculates the method execution time and catches any exception if occurring while executing the target method.
  • Get parameter information for the target method.
  • Creates a TraceEntry object and stores it in the Session, or Application scope.

Wait a minute! Why store in the Application scope?

Fair question. It sounds as if storing the tracing information within the Application isn't necessary, and Session would be good enough. Sounds correct. But, there is a problem with the Web methods, and this was the primary reason to use Application state instead of Session state. Note that, only if the current operation is a web method invocation, the tracing data is stored in the Application scope. Otherwise, these are stored inside the Session scope.

The methods which are to be traced are typically called from within the code-behind methods of ASPX or ASCX files. But, these methods can also be invoked from places which may or may not support Session (for example, web methods). So, if tracing information is always stored in Session scope, it wouldn't be possible to show the performance trace output for methods which are invoked as a result of invocation of a Web Service method (which may not support Session state). Hence, for showing tracing output for web method invocation, a separate URL is to be invoked (/TraceNet.axd) which reads the current tracing information stored within the Application scope, renders the information in the browser, and clears the tracing information from the Application.

TraceNet decides where to store trace information (Session or Application) based on whether the current operation is a web method invocation or not. Following is the class that is used to abstract the storage of objects:

C#
/// <summary>
/// Manages the storage of objects either in the Application or in the Session scope
/// </summary>
public class Storage
{
   /// <summary>
   /// Gets object from either the Session scope or Application scope
   /// </summary>
   /// <param name="Key"></param>
   /// <returns></returns>
   public static object GetObject(string Key)
   {
       object obj = null;

       if(HttpContext.Current.Session != null)
       {
            obj = HttpContext.Current.Session[Key];
       }

       if (obj == null)
       {
            obj = HttpContext.Current.Application[Key];
       }
    return obj;
   }

   /// <summary>
   /// Stores object either in the Session scope, or in the Application scope, depending upon
   /// whether the current method invocation is because of invoking a web method call or not
   /// </summary>
   /// <param name="Key"></param>
   /// <param name="Value"></param>
   /// <param name="IsWebMethod"></param>
   public static void SetObject(string Key, object Value, bool IsWebMethod)
   {
       if (IsWebMethod)
       {
           //If current operation is a web method call,
           //store the object in the Application scope
           HttpContext.Current.Application[Key] = Value;
       }
       else
       {
           //Otherwise, store the obejct in Session scope
           if (HttpContext.Current.Session != null)
           {
                HttpContext.Current.Session[Key] = Value;
           }
       }
  }
}

Now, there is a little risk that performance trace data in the Application scope for web method invocation may get overwritten by another web method invocation which may be initiated by a different user. But this is not a major problem, because even if that happens, you would get performance trace output for the same web method call. If you really need trace information of your own web method invocation, you have several options to follow:

  • Try invoking the web method multiple times.
  • Try to choose a time when your site is less busy.
  • Deploy an alternative Web Service (ASMX) only for testing purposes, having the same set of web methods (and remove it once testing is done).

Displaying trace information in a hierarchical structure, according to the call stack

The utility class PerformanceTraceOutput contains methods that process the TraceEntry list and provides the HTML output that is to be rendered in the browser. It has some distinguished methods for building the HTML outputs for exception and parameter information, and most importantly, it builds the overall trace output. Following is the method that builds the overall HTML output to show in the browser:

C#
/// <summary>
/// Class which builds tracing output for method invocation
/// </summary>
public class PerformanceTraceOutput
{   
   /// <summary>
   /// Builds the overall performance trace output
   /// </summary>
   /// <returns></returns>
   public static string GetPerformanceTraceHTML()
   {
       List<TraceEntry> traces = 
               PerformanceTracer.GetTraceEntries();

       if (traces == null || traces.Count == 0)
       {
               return string.Empty;
       }

       StringBuilder sbOutput = new StringBuilder();
       int firstIndent = traces[0].Indent;
       List<TraceEntry> currentEntries = new List<TraceEntry>();
       sbOutput = sbOutput.AppendFormat(
         "<table cellpadding=\"5px\" cellspacing=\"2px\" width=\"100%\">");
       sbOutput = sbOutput.AppendFormat("<tr style=\"background-color:{0}\"><td " + 
         "style=\"vertical-align:top;width:40px;\">{1}.</td><td" + 
         " style=\"padding-left:{2}px;\"><strong>{3} at " + 
         "{4}</strong></td></tr>", "#aaaaaa", 0, 
         10, PerformanceTracer.GetCurrentlyExecutingMethodName(), 
         PerformanceTracer.GetCurrentlyExecutingPageName());

       for (int i = 0; i < traces.Count;i++ )
       {
           TraceEntry entry = traces[i];
           sbOutput = sbOutput.AppendFormat("<tr style=\"background-color:" + 
             "{0}\"><td style=\"vertical-align:top;width:40px;\">{1}." + 
             "</td><td style=\"padding-left:{2}px;\"><strong>" + 
             "{3}</strong> executed in <span style=\"color:green\">" + 
             "<strong>{4} millisecond(s)</strong></span><span> " + 
             "{5}</span><div>{6}</div><div>{7}</div></td>" + 
             "</tr>", GetColor(i), entry.Serial, entry.Indent + 10, 
             entry.MethodName, entry.MilliSeconds, GetReturnValue(entry.ReturnValue), 
             GetParameterOutputs(entry.Parameters),GetException(entry.Exception));
       }

       sbOutput = sbOutput.AppendFormat("</table>");
       PerformanceTracer.ClearTraceEnties();
       return sbOutput.ToString();
  }
}

But this method simply outputs the trace entries stored in the Application or Session scope, and doesn't participate in calculating the call hierarchy and nesting structure of the output. Each TraceEntry object has a property Indent, and the above method simply uses this value to determine an appropriate left-padding value of the corresponding method trace information to output in HTML in a hierarchical structure.

The credit of calculating the indent and determining the call hierarchy goes to the following code in the PerformanceTracer.CalculateIndent() method:

C#
/// <summary>
/// Calculates correct indent for the current method call in the call stack
/// </summary>
/// <param name="isWebMethod"></param>
/// <returns></returns>
public static int CalculateIndent()
{
   System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace();

   for (int i = 0; i < stack.FrameCount; i++)
   {
       string name = 
         stack.GetFrame(i).GetMethod().ReflectedType.Assembly.GetName().Name;

       if (string.Compare(name, "System.Web", true) == 0)
       {
           int index = i - 2;

           if (PerformanceTracer.IsPostBack)
           {
                   index = i - 1;
           }

           string currentMethodAtPage = stack.GetFrame(index).GetMethod().Name;
           bool isWebMethod = false;

           if (currentMethodAtPage == "ProcessRequest")
           {
               index = i - 10;
               isWebMethod = true;
               currentMethodAtPage = stack.GetFrame(index).GetMethod().Name;
           }

           currentMethodAtPage = string.Format("{0}()", currentMethodAtPage);

           string currentPageName = 
             stack.GetFrame(index).GetMethod().ReflectedType.ToString();

           if (!isWebMethod)
           {
               if (string.Compare(currentPageName, "_Default", true) == 0)
               {
                       currentPageName = "Default";
               }
               currentPageName = string.Format("{0}.aspx", currentPageName);
           }
           else
           {
                   currentPageName = string.Format("{0}.asmx", currentPageName);
           }

           PerformanceTracer.SetCurrentlyExecutingMethodInfo(currentMethodAtPage, 
                                                             currentPageName);
           return i * 7;
       }
   }
   return 0;
}

This method has been written based on a study on the call stack of StackTrace, which is captured at the point when the actual method invocation takes place. Observing the call stack in a different scenario, it was possible to identify whether the current request is initiated by a web method invocation or by the execution of an ASPX page or ASCX user control. Observing the call stack further, it was also possible to identify where to find method related information in each particular scenario, and also determine an indent value for the method invocation by calculating the node distance between the position of the method invocation and the position of a certain reference point in the call stack, for that particular scenario.

CallStack

Summary

TraceNet is in its very basic version, which uses AOP, and requires minimal changes in the application to utilize and get benefited from. At present, it is capable of displaying method level tracing output only in the browser. But in future releases, it should be able to trace outputs to log files; the tracing information could be exported to Excel or CSV, and it might even require less changes in the application in order to be able to use it. Please feel free to give insightful and positive suggestions to improve this handy tool.

Happy tracing!

License

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


Written By
Founder SmartAspects
Bangladesh Bangladesh
I write codes to make life easier, and that pretty much describes me.

Comments and Discussions

 
GeneralMy vote of 5 Pin
AmitGajjar24-Apr-13 19:54
professionalAmitGajjar24-Apr-13 19:54 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey2-Apr-12 1:13
professionalManoj Kumar Choubey2-Apr-12 1:13 
GeneralAdds slight overhead on each method execution Pin
Omar Al Zabir13-May-11 6:20
Omar Al Zabir13-May-11 6:20 
GeneralRe: Adds slight overhead on each method execution Pin
Al-Farooque Shubho15-May-11 3:32
Al-Farooque Shubho15-May-11 3:32 
GeneralMy vote of 5 Pin
prasad0222-Dec-10 4:48
prasad0222-Dec-10 4:48 
GeneralMy vote of 5 Pin
linuxjr2-Dec-10 1:04
professionallinuxjr2-Dec-10 1:04 
Generalwrong spelling Pin
Jason Ti2-Dec-10 0:00
Jason Ti2-Dec-10 0:00 
GeneralRe: wrong spelling Pin
Al-Farooque Shubho2-Dec-10 0:03
Al-Farooque Shubho2-Dec-10 0:03 
GeneralMy vote of 5 Pin
Sean_15-Oct-10 2:43
Sean_15-Oct-10 2:43 
GeneralRe: My vote of 5 Pin
Al-Farooque Shubho15-Oct-10 22:55
Al-Farooque Shubho15-Oct-10 22:55 
GeneralIs it possible to use this without having to change the methods to virtual Pin
smashgeek12-Oct-10 23:52
smashgeek12-Oct-10 23:52 
GeneralRe: Is it possible to use this without having to change the methods to virtual Pin
Al-Farooque Shubho13-Oct-10 1:26
Al-Farooque Shubho13-Oct-10 1:26 
GeneralGreat article Pin
Daniel Joubert11-Oct-10 21:02
professionalDaniel Joubert11-Oct-10 21:02 
GeneralMy vote of 3 [modified] Pin
Marcello Faugiana11-Oct-10 11:48
professionalMarcello Faugiana11-Oct-10 11:48 
GeneralMy vote of 5 Pin
L Viljoen11-Oct-10 0:46
professionalL Viljoen11-Oct-10 0:46 
GeneralSuggestion Pin
Nicolas Pascual8-Oct-10 3:16
Nicolas Pascual8-Oct-10 3:16 
GeneralRe: Suggestion Pin
Al-Farooque Shubho8-Oct-10 5:56
Al-Farooque Shubho8-Oct-10 5:56 
GeneralRe: Suggestion Pin
Reiss17-Aug-11 22:22
professionalReiss17-Aug-11 22:22 
GeneralMessage Closed Pin
8-Oct-10 1:17
sandeeeoff8-Oct-10 1:17 
GeneralRe: Acai Max Cleanse Promo Code Pin
Al-Farooque Shubho8-Oct-10 1:39
Al-Farooque Shubho8-Oct-10 1:39 
GeneralMy vote of 5 Pin
Kamruzzaman Titu7-Oct-10 18:41
Kamruzzaman Titu7-Oct-10 18:41 
GeneralRe: My vote of 5 Pin
Al-Farooque Shubho7-Oct-10 18:54
Al-Farooque Shubho7-Oct-10 18:54 
GeneralFeedback Pin
Moim Hossain7-Oct-10 9:42
Moim Hossain7-Oct-10 9:42 
GeneralRe: Feedback Pin
Al-Farooque Shubho7-Oct-10 17:09
Al-Farooque Shubho7-Oct-10 17:09 
GeneralRe: Feedback Pin
Moim Hossain7-Oct-10 23:18
Moim Hossain7-Oct-10 23:18 

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.