自动记录方法的进入和退出以及参数?

时间:2008-10-05 01:02:12

标签: .net logging log4net

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

我正在使用Log4Net。

8 个答案:

答案 0 :(得分:7)

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

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

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

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

答案 1 :(得分:4)

看看:

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

同时搜索SO以寻找'AOP'或'面向方面编程'和PostSharp ......你会得到一些有趣的结果。

答案 2 :(得分:4)

我不确定你的实际需求是什么,但这是一个低租金的选择。它不完全是“自动的”,但您可以使用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 :(得分:3)

您可以使用Postsharp之类的后期编译器。网站上的示例讨论了如何设置用于输入/退出方法的跟踪器,这与您想要的非常相似。

答案 4 :(得分:0)

Decorator Pattern可能会有所帮助。

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

功能

扩展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 :(得分:0)

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

IActionFilter,IAsyncActionFilter。

public void OnActionExecuting(ActionExecutingContext context)    

public void OnActionExecuted(ActionExecutedContext context)    

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)