Как мне спроектировать логирование в моем приложении?

Поэтому я провел много исследований по этому вопросу и не нашел никаких ответов, где я сказал: "Да, ЭТО". Я надеюсь, что когда-либо эрудированная толпа Stackru поможет мне.

Я столкнулся с этой проблемой в нескольких разных сценариях. Скажем, у меня есть приложение на C#, и есть важные вещи, которые я хочу зарегистрировать.

public class MyClass
{
    ... 

    public void ImportantMethod()
    {
        DoInterestingThing();

        var result = SomethingElseImportant();
        if (result == null)
        {
            logger.Log("I wasn't expecting that. No biggie.");
            return;
        }

        MoreInterestingStuff(); 
}

То, что меня интересует, это то, где я могу получить logger от.

На мой взгляд, у меня есть несколько вариантов.

  1. Внедрите его в MyClass в конструкторе.
  2. Получите его, используя глобально доступный сервисный локатор.
  3. Используйте методы декораторов и АОП, чтобы сделать мою регистрацию для меня.

Ни один из них не кажется отличным вариантом. #3 выглядит невозможным, так как я веду журнал прямо в середине своей бизнес-логики, а не просто выполняю простую трассировку моих вызовов методов, входных параметров и / или выданных исключений. № 2, хотя простой кажется, что было бы очень трудно провести модульное тестирование. Конечно, я хотел бы проверить все. #1, хотя он и будет работать нормально, загромождает всю мою бизнес-логику регистрационными объектами , которые не имеют ничего общего с самими бизнес-объектами.

Любые альтернативные идеи или мысли по поводу одного из вариантов выше? Большое спасибо!

РЕДАКТИРОВАТЬ: просто чтобы быть ясно, я уже знаю, как сделать DI (я использую Unity), и я уже знаю хорошую структуру ведения журналов (я использую log4net). Просто интересно, как использовать логирование в смысле архитектуры в приложении самым умным способом.


* РЕДАКТИРОВАТЬ *

Я отметил ответ Марка Симана как решение. Я просмотрел свое приложение и обнаружил, что большинство моих вызовов в журнале делали то же самое, что мог сделать декоратор. А именно, зарегистрируйте запись в методе, любые исключительные ситуации и выйдите из возвращаемых значений.

В некоторых случаях мне все еще нужно было войти непосредственно в метод. Примером может быть то, где я хочу быстро потерпеть неудачу в методе, который ничего не возвращает, но не выдает исключение. В этих случаях у меня есть синглтон, который содержит ссылку на LogProvider который в свою очередь извлечет именованный экземпляр журнала. Код выглядит примерно так:

private ILog logger = LogProviderFactory.Instance.GetLogger(typeof(Foo));

LogProviderFactory имеет метод SetProvider что позволяет вам поменять синглтон. Итак, в модульном тестировании я могу сделать:

// LogProviderFactory.Instance now is our mock
LogProviderFactory.SetProvider(MockLogProvider);

Декоратор ведения журнала использует тот же LogProvider, что и синглтон (который он получает путем внедрения), поэтому ведение журнала объединяется во всей системе.

Таким образом, на самом деле конечным решением был в основном вариант № 3 и гибрид № 2 (где это шаблон локатора службы, но служба "вводится" в локатор).

АОП

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

  • Я попробовал PostSharp, но не смог правильно запустить его на своем компьютере. Кроме того, это было большим ограничением, что вы должны были установить PostSharp в вашей системе, чтобы использовать его (в отличие от простого вызова dll, который поставляется с решением или что-то подобное).
  • Я использовал LinFu и смог заставить его работать частично. Однако в нескольких случаях он взорвался. Новая версия 2.0 едва документирована, так что это было препятствием.
  • Интерфейсный перехват с Unity, однако, кажется, работает хорошо из коробки. Мне повезло, что большинство вещей, которые я хотел записать, были в классах, реализующих интерфейсы.

3 ответа

Решение

Используйте декоратор регистрации.

Два бита:

(1) - Готовая структура каротажа.

Некоторым нравится Log4Net, но я фанат EntLibs. Это делает тяжелую работу с точки зрения фактической регистрации. Такие инструменты, как EntLibs, позволят вам регистрироваться в различных типах репозиториев (база данных, очередь сообщений, текстовый файл и т. Д.). Они также позволят вам входить в различные экземпляры на основе категорий и так далее. Они обычно легко настраиваются.

(2) - Пользовательский класс (классы), который оборачивает каркас регистрации.

Таким образом, "logger" - это то, что вы пишете, и он вызывает структуру ведения журнала, чтобы выполнить фактическое ведение журнала.

Мне нравится этот подход по нескольким причинам:

  • Вы отделяете каркас ведения журнала (#1) от остальной части вашего приложения, когда пользовательские обертки переходят в отдельную сборку.
  • Написав свой собственный API ведения журнала, вы можете определить сигнатуры методов, которые соответствуют вашим потребностям, и вы можете расширить их.
  • Если вы работаете в команде, вы можете сделать подписи методов очень простыми в использовании, чтобы никто не имел оснований утверждать, что использовать ведение журнала было слишком сложно.
  • Это сохраняет логирование последовательным. Это также облегчает поиск кода для "незаконного" кода, который записывает в файлы, консоль или журнал событий, так как их не будет как часть вашей регистрации (это все в рамках).
  • Записывая определенные пользовательские классы для каждого уровня, вы можете предварительно заполнить много данных за кулисами, упрощая жизнь тому, кто пишет реальный код приложения. Вы можете установить серьезность, приоритет, идентификаторы событий по умолчанию, категории и многое другое.
  • Он хорошо масштабируется с точки зрения сложности и роста приложений; Это может показаться сложным для небольших приложений, но у вас будет много свободного места, если со временем он начнет расти.

Вот пример класса ведения информационных журналов в проекте, над которым я работал. Он имеет множество простых для вызова открытых методов и один закрытый метод, который вызывает платформу (ConcreteLogInformation).

public static void LogInformation(string title, string message)

public static void LogInformation(string title, Dictionary<string, object> extendedProperties)

public static void LogInformation(string title, int eventId, Dictionary<string, object> extendedProperties)

public static void LogInformation(string title, string message, Dictionary<string, object> extendedProperties)

public static void LogInformation(string title, string message, int eventId)

public static void LogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties)

public static void LogInformation(string title, string message, int eventId, string category)

public static void LogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties, string category)

private static void ConcreteLogInformation(string title, string message, int eventId, Dictionary<string, object> extendedProperties, string category)

В разделе Использование контекста запроса в методе Фабрика или Фабрика для создания контекстной привязки в документах Ninject Conteual Binding у меня есть пример использования вашего контейнера для внедрения соответствующего средства ведения журнала для вашего класса, выполняя (в Ninjectese):

Bind<ILog>().ToMethod( context => LogFactory.CreateLog( context.Request.Target.Type ) );

В статье о перехвате типа трассировки описывается лучший подход.

И могу ли я еще раз попросить вас подробно прочитать цитируемые статьи @Mark Seemann, прежде чем просто отбросить их без возражений.