简体   繁体   中英

How to print the flow of execution of a program in c#

I am trying to understand how I can print the execution flow of a program. I've wrote some simple code to demonstrate my dilemma.

I'd like for this to print something like: In class A, Main. In class B, methodB. In class C, methodC.

StackTrace seems to be giving me a lot of hard to read information. Is there a simpler method, or a way to have an easier to read StackTrace? The actual code is much larger and I don't want to put print statements in every single method, so I figured something like this may make debugging easier. Also, I can't use Visual Studio's debugger in the actual code. So if you guys could please advise me on which function to use, if not StackTrace, that would print the necessary information for me, I would appreciate it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StackTrace
{
        public class A
        {
            public static void Main(string[] args)
            {
                B newB = new B();
                newB.methodB();
                Console.WriteLine("StackTrace: {0}", Environment.StackTrace); //I'd like to get the entire execution flow printed here
                Console.ReadKey(); //just for testing, don't worry about this.
            }
        }

        public class B
        {

            public void methodB()
            {
                C newC = new C();
                //Console.WriteLine("In class B, method B");
                newC.methodC();
            }
        }

        public class C
        {
            public void methodC()
            {
                //Console.WriteLine("In class C, method C");
            }
        }
}

So for everyone who responded to my original thread, you may be interested in what I came up with so far. Maybe you guys, or somebody else, can add to this discussion and we can find a more permanent solution. But, here's the closest I got to automating this:

1) Create this function/method and add it to every class:

public void LogInfo(string className, string methodName)
{
   string info = ("In class: " + className + " In method: " + methodName);
   Console.WriteLine(info);
}

2) Paste this line into the relevant areas of the code base:

 StackTrace stackTrace = new StackTrace();
 LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

3) Also please take a look at my modified code, and note that we need to add these two using cases:
using System.Diagnostics; using System.Reflection;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


using System.Diagnostics;
using System.Reflection;

namespace StackTraceTest
{
        public class A
        {
            public static void Main(string[] args)
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                B newB = new B();
                newB.methodB();

                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                /*for (int i = 0; i < stackTrace.FrameCount;i++)
                {
                    LogInfo(stackTrace.GetFrame(i).GetType().Name, stackTrace.GetFrame(i).GetMethod().Name);
                }*/

                Console.ReadLine();
            }

            public static void LogInfo(string className, string methodName)
            {
                string info = ("In class: " +className +" In method: " +methodName);
                Console.WriteLine(info);
            }
        }

        public class B
        {

            public void methodB()
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);

                C newC = new C();
                newC.methodC();
            }

            public void LogInfo(string className, string methodName)
            {
                string info = ("In class: " + className + " In method: " + methodName);
                Console.WriteLine(info);
            }
        }

        public class C
        {
            public void methodC()
            {
                StackTrace stackTrace = new StackTrace();
                LogInfo(MethodBase.GetCurrentMethod().DeclaringType.Name, stackTrace.GetFrame(0).GetMethod().Name);
                //Console.WriteLine("StackTrace: {0}", Environment.StackTrace);
            }

            public void LogInfo(string className, string methodName)
            {
                string info = ("In class: " + className + " In method: " + methodName);
                Console.WriteLine(info);
            }
        }
}

4) The output now becomes:

In class: A In method: Main
In class: B In method: methodB
In class: C In method: methodC
In class: A In method: Main

5) A few things I'd like to point out to you guys: I thought the commented out for loop would be the magic bullet that would solve everything in one line, but it gives the output:

In class: StackFrame In method: Main
In class: StackFrame In method: _nExecuteAssembly
In class: StackFrame In method: ExecuteAssembly
In class: StackFrame In method: RunUsersAssembly
In class: StackFrame In method: ThreadStart_Context
In class: StackFrame In method: RunInternal
In class: StackFrame In method: Run
In class: StackFrame In method: Run
In class: StackFrame In method: ThreadStart

Also note the commented out line, in class C: If you uncomment it, it gives you the entire execution flow properly (won't post results because it includes personal info). However, this means I need to dig deep within the code, find the method that is called last, and add this line of code to it.

6) Sources: How performant is StackFrame?

How can I find the method that called the current method?

C# getting its own class name

Please let me know what you guys think. Thanks.

Using the free version of postsharp you can do this very easily. First, create an attribute you can tag on to your methods:

[PSerializable]
public class LogInvocationAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        // TODO: use your existing logging method here. simple example here for now.
        Console.WriteLine($"{args.Instance.GetType()}.{args.Method.Name}");             

        // pass the call through to the original receiver
        args.Proceed();
    }
}

After that, any method you want logged you can just apply this attribute to like this:

[LogInvocation]
public void SomeMethod(int someArg)
{
    // do stuff
}

With paid versions of postsharp you can actually apply things like this at a base class level and have it automatically apply to children as well (SO reference here ) but for what you want to do applying an attribute to your methods is a simple way to get what you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM