简体   繁体   English

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

[英]Separating Logging Code from C# Objects

Currently I have a custom built static logging class in C# that can be called with the following code: 目前我在C#中有一个自定义构建的静态日志记录类,可以使用以下代码调用:

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

When this is called it simply writes to a defined log file specified in a configuration file. 调用它时,它只会写入配置文件中指定的已定义日志文件。

However, being that I have to log many, many events, my code is starting to become hard to read because all of the logging messages. 但是,由于我必须记录许多事件,因此我的代码开始变得难以阅读,因为所有的日志消息都是如此。

Is there an established way to more or less separate logging code from objects and methods in a C# class so code doesn't become unruly? 有没有一种既定的方法来从C#类中的对象和方法中或多或少地分离日志代码,这样代码就不会变得难以驾驭?

Thank you all in advance for your help as this is something I have been struggling with lately. 提前感谢大家的帮助,因为这是我最近一直在努力的事情。

I like the AOP Features, that PostSharp offers. 我喜欢PostSharp提供的AOP功能。 In my opinion Loggin is an aspect of any kind of software. 在我看来,Loggin是任何软件的一个方面。 Logging isn't the main value an application should provide. 记录不是应用程序应提供的主要值。

So in my case, PostSharp always was fine. 所以在我的情况下,PostSharp总是很好。 Spring.NET has also an AOP module which could be used to achieve this. Spring.NET还有一个可用于实现此目的的AOP模块。

The most commonly used technique I have seen employs AOP in one form or another. 我见过的最常用的技术采用了一种或另一种形式的AOP

PostSharp is one product that does IL weaving as a form of AOP, though not the only way to do AOP in .NET . PostSharp是一种将IL编织作为AOP形式的产品,虽然不是在.NET中执行AOP的唯一方法

If your primary goal is to log function entry/exit points and occasional information in between, I've had good results with an Disposable logging object where the constructor traces the function entry , and Dispose() traces the exit . 如果您的主要目标是记录函数入口/出口点以及两者之间的临时信息,我使用Disposable日志记录对象获得了良好的结果,其中构造函数跟踪函数入口Dispose()跟踪出口 This allows calling code to simply wrap each method's code inside a single using statement. 这允许调用代码将每个方法的代码简单地包装在单个using语句中。 Methods are also provided for arbitrary logs in between. 还为其间的任意日志提供了方法。 Here is a complete C# ETW event tracing class along with a function entry/exit wrapper: 这是一个完整的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);
        }
    }
}

Code that uses it will look something like this: 使用它的代码看起来像这样:

public void DoWork(string foo)
{
    using (FnTraceWrap fnTrace = new FnTraceWrap())
    {
        fnTrace.TraceMessage("Doing work on {0}.", foo);
        /*
        code ...
        */
    }
}

A solution to this is to use Aspect-oriented programming in which you can separate these concerns. 解决方案是使用面向方面的编程 ,您可以在其中分离这些问题。 This is a pretty complex/invasive change though, so I'm not sure if it's feasible in your situation. 这是一个非常复杂/侵入性的变化,所以我不确定它在你的情况下是否可行。

To make the code readable, only log what you really need to (info/warning/error). 要使代码可读,只记录您真正需要的内容(信息/警告/错误)。 Log debug messages during development, but remove most when you are finished. 在开发期间记录调试消息,但在完成后删除最多。 For trace logging, use AOP to log simple things like method entry/exit (if you feel you need that kind of granularity). 对于跟踪日志记录,使用AOP记录方法输入/退出等简单事项(如果您觉得需要这种粒度)。

Example: 例:

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;
}

In this example, the only necessary log statement is B. The log statements A and C are boilerplate, logging that you can leave to PostSharp to insert for you instead. 在此示例中,唯一必需的日志语句是B.日志语句A和C是样板,记录您可以留给PostSharp为您插入。

Also: in your example you can see that there is some form of "Action X invoked by Y", which suggests that a lot of your code could in fact be moved up to a higher level (eg Command/Filter). 另外:在您的示例中,您可以看到存在某种形式的“由Y调用的Action X”,这表明您的许多代码实际上可以移动到更高级别(例如,命令/过滤器)。

Your proliferation of logging statements could be telling you something: that some form of design pattern could be used, which could also centralize a lot of the logging. 您的日志记录声明可能会告诉您一些事情:可以使用某种形式的设计模式,这也可以集中大量日志记录。

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

I used to have a custom built logger but recently changed to TracerX. 我曾经有一个自定义的记录器,但最近改为TracerX。 This provides a simple way to instrument the code with different levels of severity. 这提供了一种简单的方法来检测具有不同严重性级别的代码。 Loggers can be created with names closely related to the class etc that you are working with 可以使用与您正在使用的类等密切相关的名称创建记录器

It has a separate Viewer with a lot of filtering capabilities including logger, severity and so on. 它有一个单独的Viewer,具有许多过滤功能,包括记录器,严重性等。

http://tracerx.codeplex.com/ http://tracerx.codeplex.com/

There is an article on it here: http://www.codeproject.com/KB/dotnet/TracerX.aspx 这里有一篇文章: http//www.codeproject.com/KB/dotnet/TracerX.aspx

I think it is a good option to implement something similar to filters in ASP.NET MVC. 我认为在ASP.NET MVC中实现类似于过滤器的东西是一个很好的选择。 This is implement with the help of attributes and reflection. 这是在属性和反射的帮助下实现的。 You mark every method you want to log in a certain way and enjoy. 您标记要以某种方式登录并享受的每种方法。 I suppose there might be a better way to do it, may be with the help of Observer pattern or something but as long as I thought about it I couldn't think of something better. 我想可能有更好的方法来实现它,可能是在Observer模式或其他方面的帮助下,但只要我想到它我就无法想到更好的东西。

Basically such problems are called cross-cutting concerns and can be tackled with the help of AOP. 基本上这些问题被称为跨领域问题,可以在AOP的帮助下解决。

I also think that some interesting inheritance schema can be applied with log entities at the base but I would go for filters 我还认为一些有趣的继承模式可以应用于基础上的日志实体,但我会选择过滤器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM