日志记录方法设计

3

我正在用C#编写一个函数,用来记录用户在我们系统中的操作并将它们保存到数据库中。具体而言,我想记录某些业务逻辑函数被调用的情况。我找到了以下这种日志记录方法:

public static LogMethod(string user, string methodName, object[] parameters, string message)

在该方法内,对每个参数调用适当的ToString()函数。例如,在方法Foo中,它被调用如下:

void Foo(int a, SomeObject b)
{
     Logger.LogMethod(username, "Foo", new object[]{a,b}, "Beginning Foo");
     //etc
}

记录函数调用是一种好的方式吗?实现这个目的的最佳实践是什么?这样做会对性能产生不良影响吗?


你能否只使用Microsoft的Logging Application Block - Joshua Drake
1
FYI - log4net已经内置了数据库集成功能,可能会很有用:http://logging.apache.org/log4net/release/config-examples.html - Steve Townsend
4个回答

3

是的,它会影响性能,因为你需要使用反射来获取MethodInfo并调用它。除此之外,它还会破坏可维护性,因为你无法重构方法名称。

请查看以下先前的问答以获取替代方案:如何在C#中拦截方法调用?


3
不要用日志逻辑来污染你的业务逻辑,这是一个横切关注点。你应该采用像方法拦截这样的技术来拦截你想要记录的方法。

记录函数调用是一种好方法吗?

老实说,不是。现在你的代码既要执行业务逻辑,又要进行日志记录。这样的代码很难写、测试和维护。

有哪些最佳实践来实现此目的?

我鼓励你找到一种解决此问题的方法,而不是在代码中到处添加日志记录代码。标准的解决方案是使用像方法拦截或面向方面编程这样的东西。有多种方式可以实现这一点(你可以使用许多框架来设置它,但我喜欢Castle Windsor)。

这会影响性能吗?

只有你可以回答这个问题。如果你要记录像用户正在做什么这样的事情,那么不太可能。用户很慢,很笨,他们使用像磁盘和数据库这样的缓慢东西,所以不太可能在意你花费额外的几毫秒来记录一些信息。我们不是在谈论关键循环。但只有你能决定它是否过慢,只有你能测量自己的应用程序并比较记录和不记录的性能来判断是否过慢。

非常感谢,起初我考虑使用Spring、Unity或Castle Windsor等工具,但学习它们并将它们结合到我的项目中可能会耗费太多时间。你的评论引起了我的兴趣,让我更多地了解AOP解决方案,最终我选择了PostSharp,它看起来非常适合我的需求。谢谢。 - hhsaffar

2

正如其他人已经指出的,使用AOP框架可能是更好的选择。


如果你仍想使用你的方法,你可以对它进行小的改进。如果你把参数放在最后,你可以使用params关键字。

public static LogMethod(string user, string methodName, string message,
                        params object[] parameters)

这使得调用该方法变得更加简单。
void Foo(int a, SomeObject b)
{
    Logger.LogMethod(username, "Foo", "Beginning Foo", a, b);
    //etc  
}  

2

所有的因素都会影响性能,但是影响是否“不好”是主观的。这也完全取决于你调用方法的频率。

如果重新排列参数并使用params关键字,可以简化调用:

public static LogMethod(string user, string methodName, string message, params object[] parameters)

现在您的通话更简单了:
Logger.LogMethod(username, "Foo", "Beginning Foo", a, b);

不需要声明对象数组,编译器会为您处理。请参见params
此外,您可以通过从堆栈获取调用方法名称进一步简化调用。
public static LogMethod(string user, string message, params object[] parameters)
{
    string methodName = new StackFrame(1).GetMethod().Name;
    ...      
}

现在您不需要methodName参数。(在.NET 4.5中,您可以使用CallerMemberNameAttribute实现相同的功能。)

通过反射,您还可以更进一步;例如,您可以获取传递给调用方法的参数名称,类名等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接