Отделение регистрационного кода от объектов C # - PullRequest
10 голосов
/ 01 августа 2011

В настоящее время у меня есть собственный класс статической регистрации в C #, который можно вызвать с помощью следующего кода:

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

Когда он вызывается, он просто записывает в определенный файл журнала, указанный в файле конфигурации.

Однако из-за того, что мне приходится регистрировать много-много событий, мой код становится трудным для чтения из-за всех сообщений регистрации.

Существует ли установленный способ более или менее отделить код регистрации от объектов и методов в классе C #, чтобы код не становился неуправляемым?

Заранее всем спасибо за помощь, как этоэто то, с чем я боролся в последнее время.

Ответы [ 7 ]

4 голосов
/ 01 августа 2011

Наиболее часто используемый метод, который я видел, использует AOP в той или иной форме.

PostSharp - это один продукт, который делает ткачество IL как форму AOPХотя не единственный способ сделать АОП в .NET .

4 голосов
/ 01 августа 2011

Мне нравятся функции AOP, которые предлагает PostSharp.На мой взгляд, Loggin является аспектом любого программного обеспечения.Ведение журнала не является основным значением, которое должно предоставлять приложение.

Так что в моем случае PostSharp всегда был в порядке.Spring.NET также имеет модуль AOP, который можно использовать для достижения этой цели.

2 голосов
/ 01 января 2014

Если ваша основная цель - записать точки входа / выхода функции и случайную информацию между ними, у меня были хорошие результаты с Одноразовым регистрирующим объектом, где конструктор отслеживает функцию запись и 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 ...
        */
    }
}
2 голосов
/ 01 августа 2011

Раньше у меня был встроенный регистратор, но недавно он был заменен на TracerX.Это обеспечивает простой способ для инструментов кода с различными уровнями серьезности.Регистраторы могут быть созданы с именами, тесно связанными с классом и т. Д., С которыми вы работаете

. Он имеет отдельную программу просмотра с множеством возможностей фильтрации, включая регистратор, серьезность и т. Д.

http://tracerx.codeplex.com/

Здесь есть статья: http://www.codeproject.com/KB/dotnet/TracerX.aspx

2 голосов
/ 01 августа 2011

Чтобы сделать код читабельным, регистрируйте только то, что вам действительно нужно (информация / предупреждение / ошибка).Записывайте отладочные сообщения во время разработки, но удаляйте большинство, когда закончите.Для регистрации трассировки используйте 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 для вставки вместо вас.

Кроме того: в вашем примере вы можете увидеть, что есть какая-то форма «Действие»X вызывается Y ", что говорит о том, что большая часть вашего кода на самом деле может быть перемещена на более высокий уровень (например, Command / Filter).

Ваше распространение операторов ведения журнала может вам что-то сказать: что-томожет использоваться форма шаблона проектирования, которая также может централизовать большую часть ведения журнала.

void DoSomething(Command command, User user)
{
   Log.Info("Command {0} invoked by {1}", command, user);
   command.Process(user);
}
2 голосов
/ 01 августа 2011

Решением этого является использование Аспектно-ориентированного программирования , в котором вы можете разделить эти проблемы.Это довольно сложное / инвазивное изменение, поэтому я не уверен, выполнимо ли это в вашей ситуации.

0 голосов
/ 01 августа 2011

Я думаю, что это хороший вариант для реализации чего-то похожего на фильтры в ASP.NET MVC. Это реализуется с помощью атрибутов и рефлексии. Вы отмечаете каждый способ, которым вы хотите войти определенным образом и наслаждаться Я полагаю, что мог бы быть лучший способ сделать это, может быть с помощью паттерна Observer или чего-то еще, но пока я думал об этом, я не мог придумать что-то лучшее.

По сути, такие проблемы называются сквозными и могут решаться с помощью АОП.

Я также думаю, что некоторая интересная схема наследования может быть применена к сущностям журнала на базе, но я бы пошел на фильтры

...