【问题标题】:How can I add a Trace() to every method call in C#?如何将 Trace() 添加到 C# 中的每个方法调用?
【发布时间】:2009-02-17 23:12:22
【问题描述】:

我很难追踪锁定问题,所以我想记录每个方法调用的进入和退出。我以前用 C++ 做过这个,而不必为每个方法添加代码。这可以用 C# 实现吗?

【问题讨论】:

  • 既然你知道你要锁定什么以及它大致在哪里,那么发布一些代码怎么样......
  • 请注意,这样做很可能会显着改变您的应用程序的时间,从而使您的锁定问题消失。
  • 通过自动添加额外的函数调用来更改代码失败的环境可能会将状态修改为不再发生死锁的点。发布代码可能是解决问题的最佳方法。
  • 非常正确 - 即使在应用程序运行时将调试器挂接到应用程序也可以防止问题发生。我最终没有走这条路,我使用 WinDbg 和几十个堆栈跟踪转储找到了原因。
  • 只是为了记录,我最终没有走这条路。我最终使用 WinDbg 和几十个堆栈跟踪转储解决了这个问题。

标签: c# logging trace


【解决方案1】:

您最好的选择可能是使用 AOP(面向方面​​编程)框架在方法执行之前和之后自动调用跟踪代码。 AOP 和 .NET 的流行选择是PostSharp

【讨论】:

【解决方案2】:

如果您的主要目标是记录函数入口/出口点以及介于两者之间的偶尔信息,那么我使用 Disposable 记录对象取得了不错的结果,构造函数在其中跟踪函数入口Dispose() 跟踪出口。这允许调用代码简单地将每个方法的代码包装在单个 using 语句中。还为中间的任意日志提供了方法。这是一个完整的 C# ETW 事件跟踪类以及一个函数进入/退出包装器:

using System;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace MyExample
{
    // This class traces function entry/exit
    // Constructor is used to automatically log function entry.
    // Dispose is used to automatically log function exit.
    // use "using(FnTraceWrap x = new FnTraceWrap()){ function code }" pattern for function entry/exit tracing
    public class FnTraceWrap : IDisposable
    {
        string methodName;
        string className;

        private bool _disposed = false;

        public FnTraceWrap()
        {
            StackFrame frame;
            MethodBase method;

            frame = new StackFrame(1);
            method = frame.GetMethod();
            this.methodName = method.Name;
            this.className = method.DeclaringType.Name;

            MyEventSourceClass.Log.TraceEnter(this.className, this.methodName);
        }

        public void TraceMessage(string format, params object[] args)
        {
            string message = String.Format(format, args);
            MyEventSourceClass.Log.TraceMessage(message);
        }

        public void Dispose()
        {
            if (!this._disposed)
            {
                this._disposed = true;
                MyEventSourceClass.Log.TraceExit(this.className, this.methodName);
            }
        }
    }

    [EventSource(Name = "MyEventSource")]
    sealed class MyEventSourceClass : EventSource
    {
        // Global singleton instance
        public static MyEventSourceClass Log = new MyEventSourceClass();

        private MyEventSourceClass()
        {
        }

        [Event(1, Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
        public void TraceMessage(string message)
        {
            WriteEvent(1, message);
        }

        [Event(2, Message = "{0}({1}) - {2}: {3}", Opcode = EventOpcode.Info, Level = EventLevel.Informational)]
        public void TraceCodeLine([CallerFilePath] string filePath = "",
                                  [CallerLineNumber] int line = 0,
                                  [CallerMemberName] string memberName = "", string message = "")
        {
            WriteEvent(2, filePath, line, memberName, message);
        }

        // Function-level entry and exit tracing
        [Event(3, Message = "Entering {0}.{1}", Opcode = EventOpcode.Start, Level = EventLevel.Informational)]
        public void TraceEnter(string className, string methodName)
        {
            WriteEvent(3, className, methodName);
        }

        [Event(4, Message = "Exiting {0}.{1}", Opcode = EventOpcode.Stop, Level = EventLevel.Informational)]
        public void TraceExit(string className, string methodName)
        {
            WriteEvent(4, className, methodName);
        }
    }
}

使用它的代码如下所示:

public void DoWork(string foo)
{
    using (FnTraceWrap fnTrace = new FnTraceWrap())
    {
        fnTrace.TraceMessage("Doing work on {0}.", foo);
        /*
        code ...
        */
    }
}

【讨论】:

    【解决方案3】:

    分析器非常适合在开发期间查看正在运行的代码,但如果您正在寻找在生产中进行自定义跟踪的能力,那么正如 Denis G. 所提到的,PostSharp 是完美的工具:您不需要无需更改所有代码,您可以轻松打开/关闭它。

    几分钟内即可轻松设置,PostSharp 的创建者 Gaël Fraiteur 甚至提供视频,向您展示向现有应用添加跟踪是多么容易。
    您可以在documentation section 中找到示例和教程。

    【讨论】:

      【解决方案4】:

      使用 Red Gate 的 ANTS Profiler 将是您的最佳选择。如果做不到这一点,请查看Castle Windsor 中的interceptors。不过,这确实假设您正在通过 IoC 加载您的类型。

      反射是另一种方式,您可以使用System.Reflection.Emit 方法将代码“写入”到内存中。该代码可以替换您的方法的代码,并执行它但使用适当的日志记录。不过,祝你好运……更容易的是使用像Aspect#这样的面向方面的编程框架。

      【讨论】:

      • ANTS 很棒(就像 VS 内置的分析器一样),但缺点是您可能会更改应用程序的内存/计时“足迹”。并且可能不会遇到锁定...
      • 跟踪也会改变时间。
      • 是的,这是海森堡的不确定性原理。通过观察实验,你很可能会改变实验的结果。那是你的线程同步调试......
      【解决方案5】:

      它可能正在等待锁定问题发生,执行内存转储并分析各种线程上的调用堆栈。您可以使用DebugDiagDebugging Tools for Windows 附带的adplus 脚本(在本例中为挂起模式)。

      Tess Ferrandez 也有一个excellent lab series 学习使用 .NET 内存转储调试各种问题。我强烈推荐它。

      【讨论】:

        【解决方案6】:

        你怎么知道它正在发生?如果这是一个多线程应用程序,我建议在运行时测试条件并在检测到它时调用 System.Diagnostics.Debugger.Break()。然后,只需打开 Threads 窗口并单步执行每个相关线程上的调用堆栈。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-04-07
          • 2022-11-28
          • 2022-11-17
          • 2019-05-28
          • 1970-01-01
          • 2021-03-16
          • 2013-03-31
          相关资源
          最近更新 更多