Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#

How to log the current call stack in .NET

Rate me:
Please Sign up or sign in to vote.
4.69/5 (11 votes)
10 Jul 2011CPOL1 min read 99.6K   1.6K   45   10
This article explains how to log the current call stack by using the StackFrame and StackTrace classes of the .NET Framework.

Introduction

Tracing method calls to a log file is a troubleshooting technique used when a standard debug session is not possible (for example, when a given problem can only be reproduced in the production environment or in the customer's computer). When the method to be traced can be called in more than one context, simply logging the name and the parameters of such a method is not enough for understanding the application's workflow.

One option is to add tracing code to all methods which call the given method. An easier solution is tracing the current call stack. The StackFrame and StackTrace classes, belonging to the System.Diagnostics namespace of the .NET Framework, can be used for this purpose.

How to use StackFrame and StackTrace

The StackFrame class (see the online MSDN library) represents a method call on the current call stack. The StackTrace class (see the online MSDN library) is a collection of StackFrame objects. Therefore, by means of the StackTrace constructor, a snapshot of the current call stack can be created.

Moreover, the StackTrace.ToString method returns a string representation:

C#
public static class Logger
{
  ...
  
  public static string CurrentStackDefaultLog()
  {
    // the true value is used to include source file info
    var l_CurrentStack = new System.Diagnostics.StackTrace(true);
    return l_CurrentStack.ToString();
  }
  
  ...
}

An example of its output follows (that is similar to the output of the Exception.ToString method):

at Mazzeranghi.StackLogger.Logger.CurrentStackDefaultLog() 
   in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\Logger.cs:line 54
at Mazzeranghi.StackLogger.Logger.AddCurrentStackDefaultLog() 
   in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\Logger.cs:line 48
at Mazzeranghi.StackLogger.BankAccount.Withdraw(Int32 p_WithdrawalValue) 
   in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\BankAccount.cs:line 39
at Mazzeranghi.StackLogger.MainFrame.ValidOperationWithDefaultLog_Click(Object p_Sender, 
   EventArgs p_EventArgs) 
   in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\MainFrame.cs:line 41
at System.Windows.Forms.Control.OnClick(EventArgs e)
...

But, if you want to customize such a string representation (for instance, in order to remove the lines corresponding to Logger methods), several methods of the StackFrame class are available:

C#
public static class Logger
{
  ...

  public static String CurrentStackCustomizedLog()
  {
    var l_StackLog = new System.Text.StringBuilder();
    var l_CurrentStack = new System.Diagnostics.StackTrace(true);
    // the true value is used to include source file info

    for (Int32 x = 0; x < l_CurrentStack.FrameCount; ++x)
    {
      var l_MethodCall = l_CurrentStack.GetFrame(x);
      if (IsMethodToBeIncluded(l_MethodCall))
        l_StackLog.AppendLine(MethodCallLog(l_MethodCall));
    }
    return l_StackLog.ToString();
  }

  // This method is used to keep Logger methods out of the returned log
  // (the methods actually included in a StackTrace
  // depend on compiler optimizations).
  private static bool IsMethodToBeIncluded(
          System.Diagnostics.StackFrame p_StackMethod)
  {
    var l_Method = p_StackMethod.GetMethod();
    if (l_Method.DeclaringType == typeof(Logger))
      return false;
    else
      return true;
  }

  // Instead of visiting each field of stackFrame,
  // the StackFrame.ToString() method could be used, 
  // but the returned text would not include the class name.
  private static String MethodCallLog(System.Diagnostics.StackFrame p_MethodCall)
  {
    System.Text.StringBuilder l_MethodCallLog = 
                new System.Text.StringBuilder();

    var l_Method = p_MethodCall.GetMethod();
    l_MethodCallLog.Append(l_Method.DeclaringType.ToString());
    l_MethodCallLog.Append(".");
    l_MethodCallLog.Append(p_MethodCall.GetMethod().Name);

    var l_MethodParameters = l_Method.GetParameters();
    l_MethodCallLog.Append("(");
    for (Int32 x = 0; x < l_MethodParameters.Length; ++x)
    {
      if (x > 0)
        l_MethodCallLog.Append(", ");
      var l_MethodParameter = l_MethodParameters[x];
      l_MethodCallLog.Append(l_MethodParameter.ParameterType.Name);
      l_MethodCallLog.Append(" ");
      l_MethodCallLog.Append(l_MethodParameter.Name);
    }
    l_MethodCallLog.Append(")");

    var l_SourceFileName = p_MethodCall.GetFileName();
    if (!String.IsNullOrEmpty(l_SourceFileName))
    {
      l_MethodCallLog.Append(" in ");
      l_MethodCallLog.Append(l_SourceFileName);
      l_MethodCallLog.Append(": line ");
      l_MethodCallLog.Append(p_MethodCall.GetFileLineNumber().ToString());
    }

    return l_MethodCallLog.ToString();
  }
  
  ...
}

An output example follows:

Mazzeranghi.StackLogger.BankAccount.Deposit(Int32 p_DepositValue) 
    in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\BankAccount.cs: line 31
Mazzeranghi.StackLogger.MainFrame.ValidOperationWithCustomizedLog_Click(Object p_Sender, 
    EventArgs p_EventArgs) 
    in C:\VS2010Express\Projects\Mazzeranghi.StackLogger\MainFrame.cs: line 48
System.Windows.Forms.Control.OnClick(EventArgs e)
...

Sample code

A sample C# application is also available for testing the following cases:

  • Exception.ToString log (see button "Invalid Operation").
  • StackTrace default log (see button "Valid Operation (with default log)").
  • StackTrace customized log (see button "Valid Operation (with customized log)").

Sample Application

License

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


Written By
Technical Lead Esaote
Italy Italy

Since 1998, I'm a software project leader at Esaote, one of the world's major manufacturers of non-invasive diagnostic equipment and system, as well as the leading European manufacturer of ultrasound imaging equipment.


My professional skills include:



  • Programming Languages: C#, C/C++, ASP.NET, XML, XSLT, Prolog.
  • Libraries: .NET Framework, STL, MFC.
  • Paradigms and Standards: Object-Oriented Programming, Design Patterns, Code Generation, Extreme Programming, Design by Contract, Dicom.
  • Tools: Visual Studio .NET, SQL Server.

You can contact me at my Web site (www.mazzeranghi.com).


Comments and Discussions

 
QuestionHow to extract the value passed to each function? Pin
tomdromi15-Jul-11 21:33
tomdromi15-Jul-11 21:33 
AnswerRe: How to extract the value passed to each function? Pin
Daniele Mazzeranghi18-Jul-11 9:20
Daniele Mazzeranghi18-Jul-11 9:20 
QuestionHello Pin
Yves12-Jul-11 13:57
Yves12-Jul-11 13:57 
AnswerRe: Hello Pin
Daniele Mazzeranghi14-Jul-11 11:00
Daniele Mazzeranghi14-Jul-11 11:00 
QuestionStack walking is slow Pin
Pavels Mihailovs11-Jul-11 20:15
Pavels Mihailovs11-Jul-11 20:15 
AnswerRe: Stack walking is slow Pin
Daniele Mazzeranghi12-Jul-11 9:51
Daniele Mazzeranghi12-Jul-11 9:51 
GeneralRe: Stack walking is slow Pin
Pavels Mihailovs12-Jul-11 19:46
Pavels Mihailovs12-Jul-11 19:46 
QuestionCaveat Pin
Yvan Rodrigues11-Jul-11 15:38
professionalYvan Rodrigues11-Jul-11 15:38 
AnswerRe: Caveat Pin
Daniele Mazzeranghi12-Jul-11 9:18
Daniele Mazzeranghi12-Jul-11 9:18 
GeneralMy vote of 5 Pin
Rhuros10-Jul-11 21:55
professionalRhuros10-Jul-11 21:55 

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.