【问题标题】:Logging entry and exit of methods along with parameters automagically?自动记录方法的进入和退出以及参数?
【发布时间】:2008-10-05 01:02:11
【问题描述】:

有没有办法让我添加日志记录,以便以某种方式自动记录进入和退出方法以及参数以用于跟踪目的?我该怎么做?

我正在使用 Log4Net。

【问题讨论】:

    标签: .net logging log4net


    【解决方案1】:

    实现这种事情的最好方法是使用拦截,有几种方法可以做到这一点,尽管它们都倾向于有点侵入性。一种是从 ContextBoundObject 派生所有对象。 Here 是使用这种方法的一个例子。另一种方法是使用现有的 AOP 库之一来实现这一点。 Castle Project 中的 DynamicProxy 之类的东西是其中许多的核心。这里有几个链接: Spring.Net PostSharp Cecil

    可能还有其他几个,我知道Castle WindsorNinject 都在 IoC 功能之上提供 AOP 功能。

    一旦 AOP 到位,您只需编写一个拦截器类,它将有关方法调用的信息写入 log4net。

    如果其中一个 AOP 框架能够为您提供开箱即用的那种功能,我实际上不会感到惊讶。

    【讨论】:

      【解决方案2】:

      我不确定您的实际需求是什么,但这里有一个低租金的选择。它不是完全“自动”的,但您可以使用 StackTrace 以不需要传递参数的方式剥离您正在寻找的信息 - 类似于 ckramer 关于拦截的建议:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      using System.Diagnostics;
      
      namespace TracingSample
      {
          class Program
          {
              static void Main(string[] args)
              {
                  DoSomething();
              }
      
              static void DoSomething()
              {
                  LogEnter();
      
                  Console.WriteLine("Executing DoSomething");
      
                  LogExit();
              }
      
              static void LogEnter()
              {
                  StackTrace trace = new StackTrace();
                  if (trace.FrameCount > 1)
                  {
                      string ns = trace.GetFrame(1).GetMethod().DeclaringType.Namespace;
                      string typeName = trace.GetFrame(1).GetMethod().DeclaringType.Name;
                      Console.WriteLine("Entering {0}.{1}.{2}", ns, typeName, trace.GetFrame(1).GetMethod().Name);
                  }
              }
      
              static void LogExit()
              {
                  StackTrace trace = new StackTrace();
                  if (trace.FrameCount > 1)
                  {
                      string ns = trace.GetFrame(1).GetMethod().DeclaringType.Namespace;
                      string typeName = trace.GetFrame(1).GetMethod().DeclaringType.Name;
                      Console.WriteLine("Exiting {0}.{1}.{2}", ns, typeName, trace.GetFrame(1).GetMethod().Name);
                  }
              }
          }
      }
      

      您可以将上述示例与继承结合起来,使用基类型中的非虚拟公共成员来表示操作方法,然后调用虚拟成员来实际完成工作:

      public abstract class BaseType
      {
          public void SomeFunction()
          {
      
              LogEnter();
              DoSomeFunction();
              LogExit();
          }
      
          public abstract void DoSomeFunction();
      }
      
      public class SubType : BaseType
      {
          public override void DoSomeFunction()
          {
              // Implementation of SomeFunction logic here...
          }
      }
      

      再说一次 - 关于这一点并没有太多“自动”,但如果您不需要对每个方法调用都进行检测,它会在有限的基础上工作。

      希望这会有所帮助。

      【讨论】:

      【解决方案3】:

      看看:

      How do I intercept a method call in C#? PostSharp - il weaving - thoughts

      同时搜索“AOP”或“面向方面的编程”和 PostSharp……你会得到一些有趣的结果。

      【讨论】:

        【解决方案4】:

        您可以使用像Postsharp 这样的后编译器。该网站的示例讨论了设置用于进入/退出方法的跟踪器,这与您想要的非常相似。

        【讨论】:

          【解决方案5】:

          您可以在具有 LogInjector 的 CodePlex 上使用开源框架 CInject 来标记方法调用的进入和退出。

          或者您可以按照本文Intercepting Method Calls using IL 中提到的步骤,使用 C# 中的 Reflection.Emit 类创建自己的拦截器。

          【讨论】:

            【解决方案6】:

            功能

            扩展 Jared 的答案,避免代码重复并包括 options for arguments

            private static void LogEnterExit(bool isEnter = true, params object[] args)
            {
                StackTrace trace = new StackTrace(true);  // need `true` for getting file and line info
                if (trace.FrameCount > 2)
                {
                    string ns = trace.GetFrame(2).GetMethod().DeclaringType.Namespace;
                    string typeName = trace.GetFrame(2).GetMethod().DeclaringType.Name;
                    string args_string = args.Length == 0 ? "" : "\narguments: [" + args.Aggregate((current, next) => string.Format("{0},{1};", current, next))+"]";
                    Console.WriteLine("{0} {1}.{2}.{3}{4}", isEnter ? "Entering" : "Exiting", ns, typeName, trace.GetFrame(2).GetMethod().Name, args_string );
                }
            }
            
            static void LogEnter(params object[] args)
            {
                LogEnterExit(true, args);
            }
            
            static void LogExit(params object[] args)
            {
                LogEnterExit(false, args);
            }
            

            用法

            static void DoSomething(string arg1)
            {
                LogEnter(arg1);
                Console.WriteLine("Executing DoSomething");
                LogExit();
            }
            

            输出

            在控制台中,如果DoSomething 以“blah”作为arg1 运行,这将是输出

            Entering Program.DoSomething
            arguments: [blah]
            Executing DoSomething
            Exiting Program.DoSomething
            

            【讨论】:

              【解决方案7】:

              这不适用于所有 C# 应用程序,但我在 Asp.Net Core 应用程序中执行以下操作,通过实现以下接口将日志记录添加到控制器方法调用:

              IActionFilter、IAsyncActionFilter。

              public void OnActionExecuting(ActionExecutingContext context)    
              
              public void OnActionExecuted(ActionExecutedContext context)    
              
              public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)    
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2012-09-25
                • 2013-01-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-10-01
                相关资源
                最近更新 更多