C#.NET-3.5 - 创建包含参数和输出的自定义、可读的跟踪方法

C#.NET-3.5 - Creating a Custom, Readable Trace Method Which Includes Params and Output

我通过执行以下内部方法向我的应用程序添加了自定义跟踪,我想了解有关这些方法的信息:

public static void MyMethod(params)
{
    pathTrace += "MyMethod(" + params + "){";

    // Some method calls and calculations etc ...

    pathTrace += INTERESTINGOUTPUT + "}";
}

一旦用户旅程结束,pathTrace 字符串就会被发送到数据库,其中包含我感兴趣的所有方法以及参数和输出,并且采用易于阅读的格式。

然而,这看起来很难看,因为我必须将 pathTrace 增量器添加到每个方法中......

任何人都可以建议一种方法,让我可以编写一个方法来为我添加这个(如果可能的话,包括参数和输出以及括号),这样我也许可以这样称呼它:

public static void MyMethd(params)
{
    pathTrace();

    // Some method calls and calculations etc ...
}

我希望包含开始 结束括号(我觉得这是困难的部分)...

非常感谢任何建议! 谢谢

两种技术,一种简单但不优雅,一种很难,你真的需要工具。

方法一:创建一个计时器class实现IDisposable

public class MyTimer:IDisposable{
     // Start timer in constructor
     // end time in Dispose()
}

当您使用

时,这会跟踪到一行代码加上花括号
public void MyMethod(){
   using (MyTimer()){
   //method body
   }
}

或者,您可以使用程序集重写,其中作为 post 编译步骤,工具会查找具有属性的方法并注入额外的代码。这是面向方面的编程,讨论了 C# 中的各种技术 here

如果您按照要求的方式调用它,则必须检查堆栈跟踪以找到调用方法的名称并进行大量反射以收集运行时参数信息,而且成本很高。你当然不能把电话留在原地。

您可以通过将一些冗余信息(例如当前方法名称)传递给您的跟踪内容来避免大部分成本。我做了类似的事情,所以我可以 "instrument" 带有跟踪包装器的方法的主体。调用可能如下所示:

static void DoSomeMethod( )
{
  Tracer.TraceRun( "DoSomeMethod", ( ) =>
  {
    //--> body of method...
    var x = DoSomeFunction( );

  } );
}

static int DoSomeFunction( )
{
  return Tracer.TraceRun( "DoSomeFunction", ( ) =>
  {

    //--> body of method...
    return 9 - DoSomeFunctionWithArgs( 3 );

  } );
}

static int DoSomeFunctionWithArgs( int arg )
{
  return Tracer.TraceRun( "DoSomeFunctionWithArgs", ( ) =>
  {

    //--> body of method...
    return 1 + arg;

  }, arg );
}

...运行 DoSomeMethod() 的预期输出为:

DoSomeMethod(){
 DoSomeFunction(){
  DoSomeFunctionWithArgs(3){
   4}
  5}
 }

为了完成这项工作,Tracer.TraceRun 被重载,这样我就可以执行方法和函数:

class Tracer
{
  [ThreadStatic]
  private static int depth = 0;
  public static void TraceRun( string name, Action method, params object[ ] args )
  {
    TraceStartCall( name, args );
    depth++;
    method( );
    TraceEndCall( );
    depth--;
  }

  public static T TraceRun<T>( string name, Func<T> function, params object[ ] args )
  {
    TraceStartCall( name, args );
    depth++;
    var results = function( );
    TraceEndCall( results );
    depth--;
    return results;
  }

  private static void TraceStartCall( string functionName, params object[ ] args )
  {
    Trace.WriteLine
    (
      string.Format
      (
        "{0}: {1}{2}({3}){{",
        Thread.CurrentThread.ManagedThreadId,
        new string( ' ', depth ),
        functionName,
        args == null ?
          string.Empty :
          string.Join( ", ", Array.ConvertAll( args, i => i.ToString( ) ).ToArray( ) )
      )
    );
  }

  private static void TraceEndCall( object results = null )
  {
    Trace.WriteLine
    (
      string.Format
      (
        "{0}: {1}{2}}}",
        Thread.CurrentThread.ManagedThreadId,
        new string( ' ', depth ),
        results
      )
    );
  }


}

此代码以廉价且简单的方式处理线程管理问题...只需输出 threadId,以便您可以查看线程如何互操作(并使用 ThreadStatic 标记 Tracer 的深度)。当我 运行 时输出看起来像这样:

10: DoSomeMethod(){
10:  DoSomeFunction(){
10:   DoSomeFunctionWithArgs(3){
10:    4}
10:   5}
10:  }