将记录代码与C#对象分离

时间:2011-08-01 13:31:31

标签: c# logging error-logging

目前我在C#中有一个自定义构建的静态日志记录类,可以使用以下代码调用:

EventLogger.Log(EventLogger.EventType.Application, string.Format("AddData request from {0}", ipAddress));

调用它时,它只会写入配置文件中指定的已定义日志文件。

但是,由于我必须记录许多事件,因此我的代码开始变得难以阅读,因为所有的日志消息都是如此。

是否有一种既定方法可以从C#类中的对象和方法中或多或少地分离日志代码,这样代码就不会变得难以驾驭?

提前感谢大家的帮助,因为这是我最近一直在努力的事情。

7 个答案:

答案 0 :(得分:4)

我喜欢PostSharp提供的AOP功能。在我看来,Loggin是任何软件的一个方面。记录不是应用程序应提供的主要值。

所以在我的情况下,PostSharp总是很好。 Spring.NET还有一个可用于实现此目的的AOP模块。

答案 1 :(得分:4)

我见过的最常用的技术是以一种或另一种形式使用AOP

PostSharp是一种将IL编织作为AOP形式的产品,尽管是not the only way to do AOP in .NET

答案 2 :(得分:2)

对此的解决方案是使用Aspect-oriented programming来分隔这些问题。这是一个相当复杂/侵入性的变化,所以我不确定它是否适合你的情况。

答案 3 :(得分:2)

要使代码可读,只记录您真正需要的内容(信息/警告/错误)。在开发期间记录调试消息,但在完成后删除最多。对于跟踪日志记录,请使用 AOP记录方法进入/退出等简单的事情(如果你觉得你需要那种粒度)。

示例:

public int SomeMethod(int arg)
{
   Log.Trace("SomeClass.SomeMethod({0}), entering",arg);  // A
   if (arg < 0)
   {
      arg = -arg;
      Log.Warn("Negative arg {0} was corrected", arg);    // B
   }
   Log.Trace("SomeClass.SomeMethod({0}), returning.",arg);  // C
   return 2*arg;
}

在这个例子中,唯一必要的日志语句是B.日志语句A和C是样板,记录您可以留给PostSharp为您插入。

另外:在您的示例中,您可以看到某种形式的“Y调用的Action X”,这表明您的许多代码实际上可以移动到更高级别(例如命令/过滤器)。

日志记录语句的激增可能会告诉您一些事情:可以使用某种形式的设计模式,这也可以集中大量日志记录。

void DoSomething(Command command, User user)
{
   Log.Info("Command {0} invoked by {1}", command, user);
   command.Process(user);
}

答案 4 :(得分:2)

我曾经有一个自定义构建的记录器,但最近改为TracerX。这提供了一种简单的方法来检测具有不同严重性级别的代码。可以使用与您正在使用的类等密切相关的名称创建记录器

它有一个单独的Viewer,具有很多过滤功能,包括记录器,严重性等。

http://tracerx.codeplex.com/

这里有一篇文章:http://www.codeproject.com/KB/dotnet/TracerX.aspx

答案 5 :(得分:2)

如果您的主要目标是记录功能进入/退出点以及两者之间的偶然信息,我使用一次性日志记录对象获得了良好的结果,其中 构造函数跟踪功能输入 Dispose()跟踪退出 。这允许调用代码简单地将每个方法的代码包装在单个使用语句中。还为其间的任意日志提供了方法。这是一个完整的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 ...
        */
    }
}

答案 6 :(得分:0)

我认为在ASP.NET MVC中实现与过滤器类似的东西是一个很好的选择。这是在属性和反射的帮助下实现的。您标记要以某种方式登录并享受的每种方法。我想可能有更好的方法可以做到,可能是在Observer模式或其他方面的帮助下,但只要我想到它我就无法想到更好的东西。

基本上这些问题被称为跨领域问题,可以在AOP的帮助下解决。

我还认为一些有趣的继承架构可以应用于基础上的日志实体,但我会选择过滤器