Хорошая практика заключается в том, что логгер как одноэлементный?

У меня была привычка передавать регистратор в конструктор, например:

public class OrderService : IOrderService {
     public OrderService(ILogger logger) {
     }
}

Но это довольно раздражает, поэтому я использовал это свойство в течение некоторого времени:

private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
    get { return logger; }
    set { logger = value; }
}

Это тоже раздражает - это не сухо, мне нужно повторить это в каждом классе. Я мог бы использовать базовый класс, но опять же - я использую класс Form, поэтому понадобится FormBase и т.д. Поэтому я думаю, что было бы недостатком синглтона с выставленным ILogger, поэтому кто-то знал бы, где взять регистратор:

    Infrastructure.Logger.Info("blabla");

UPDATE: Как правильно заметил Мерлин, я должен упомянуть, что в первом и втором примерах я использую DI.

Ответ 1

Это тоже раздражает - это не DRY

Это правда. Но есть только так много, что вы можете сделать для сквозной заботы, которая пронизывает каждый тип, который у вас есть. Вы должны использовать логгер везде, поэтому у вас должно быть свойство на этих типах.

Итак, посмотрим, что мы можем с этим сделать.

Singleton

Синглтоны ужасны <flame-suit-on>.

Я рекомендую придерживаться инъекции свойств, как вы это сделали со вторым примером. Это лучший факторинг, который вы можете сделать, не прибегая к магии. Лучше иметь явную зависимость, чем скрывать ее с помощью одноэлементного.

Но если синглоты сэкономят вам значительное время, включая все рефакторинги, которые вам когда-либо придется делать (время хрустального шара!), я полагаю, вы могли бы жить с ними. Если когда-либо было использование Singleton, возможно, это и есть. Имейте в виду, что стоимость, если вы когда-либо захотите изменить свой разум, будет примерно такой же высокой, как и она.

Если вы это сделаете, ознакомьтесь с ответами других людей, используя шаблон Registry (см. описание), и те, кто регистрирует (сбрасывается ) singleton factory, а не одиночный экземпляр журнала.

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

фрагменты кода Visual Studio

Вы можете использовать фрагменты кода Visual Studio, чтобы ускорить вход этого повторяющегося кода. Вы можете ввести что-то вроде logger tab, и код будет волшебным образом отображаться для вас.

Использование AOP для DRY off

Вы можете устранить немного этого кода инъекции свойств, используя структуру AOP-ориентированного программирования (AOP), такую ​​как PostSharp, чтобы автоматически генерировать некоторые его.

Это может выглядеть примерно так, когда вы закончите:

[InjectedLogger]
public ILogger Logger { get; set; }

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

[Trace]
public class MyClass
{
    // ...
}

// or

#if DEBUG
[assembly: Trace( AttributeTargetTypes = "MyNamespace.*",
    AttributeTargetTypeAttributes = MulticastAttributes.Public,
    AttributeTargetMemberAttributes = MulticastAttributes.Public )]
#endif

Ответ 2

Я помещаю экземпляр журнала в контейнер для инъекций зависимостей, который затем вводит регистратор в классы, которые нуждаются в нем.

Ответ 3

Хороший вопрос. Я считаю, что в большинстве проектов logger - одноэлементный.

Некоторые идеи просто приходят мне в голову:

  • Используйте ServiceLocator (или другой контейнер Зависимость впрыска, если вы уже используете какой-либо), который позволяет вам share logger через службы/классы, таким образом вы можете создать экземпляр регистратора или даже нескольких разных регистраторов и поделиться через ServiceLocator, что, очевидно, будет одноэлементным, своего рода Inversion of Control. Такой подход дает вам большую гибкость в отношении процесса создания экземпляра журнала и процесса инициализации.
  • Если вам нужен регистратор почти везде - внедрите методы расширения для типа Object, чтобы каждый класс мог вызвать методы журнала, такие как LogInfo(), LogDebug(), LogError()

Ответ 4

Синтаксис - хорошая идея. Еще лучшая идея - использовать шаблон Registry, который дает немного больше контроля над созданием экземпляра. По-моему, одноэлементный шаблон слишком близок к глобальным переменным. При создании или повторном использовании объекта обработки реестра есть место для будущих изменений в правилах создания экземпляров.

Сам реестр может быть статическим классом, чтобы дать простой синтаксис для доступа к журналу:

Registry.Logger.Info("blabla");

Ответ 5

Простой синглтон - это не очень хорошая идея. Это затрудняет замену регистратора. Я использую фильтры для своих регистраторов (некоторые "шумные" классы могут регистрировать только предупреждения/ошибки).

Я использую одноэлементный шаблон в сочетании с шаблоном прокси для журнала factory:

public class LogFactory
{
    private static LogFactory _instance;

    public static void Assign(LogFactory instance)
    {
        _instance = instance;
    }

    public static LogFactory Instance
    {
        get { _instance ?? (_instance = new LogFactory()); }
    }

    public virtual ILogger GetLogger<T>()
    {
        return new SystemDebugLogger();
    }
}

Это позволяет мне создать FilteringLogFactory или просто SimpleFileLogFactory без изменения кода (и, следовательно, соблюдая принцип Open/Closed).

Расширение образца

public class FilteredLogFactory : LogFactory
{
    public override ILogger GetLogger<T>()
    {
        if (typeof(ITextParser).IsAssignableFrom(typeof(T)))
            return new FilteredLogger(typeof(T));

        return new FileLogger(@"C:\Logs\MyApp.log");
    }
}

И использовать новый factory

// and to use the new log factory (somewhere early in the application):
LogFactory.Assign(new FilteredLogFactory());

В вашем классе, который должен регистрироваться:

public class MyUserService : IUserService
{
    ILogger _logger = LogFactory.Instance.GetLogger<MyUserService>();

    public void SomeMethod()
    {
        _logger.Debug("Welcome world!");
    }
}

Ответ 6

В .NET есть книга "Инъекция зависимостей". Основываясь на том, что вам нужно, вы должны использовать перехват.

В этой книге есть диаграмма, помогающая решить, использовать ли инъекцию конструктора, инъекцию свойств, инъекцию метода, контекст окружения, перехват.

То, как одна из причин использует эту диаграмму:

  • У вас есть зависимость или она нужна? - Нужна она
  • Является ли это сквозной проблемой? - Да
  • Вам нужен ответ? - Нет

Использовать перехват

Ответ 7

Если вы хотите посмотреть на хорошее решение для ведения журнала, я предлагаю вам взглянуть на движок Google с помощью python, где ведение журнала так же просто, как import logging, а затем вы можете просто logging.debug("my message") или logging.info("my message"), который действительно сохраняет его как просто, как и должно быть.

У Java не было хорошего решения для регистрации, т.е. log4j следует избегать, поскольку он практически заставляет вас использовать синглтоны, которые, как сказано здесь, "ужасны", и у меня был ужасный опыт с попыткой сделать вывод журнала одним и тем же журналом выражение только один раз, когда я подозреваю, что причиной двойного ведения журнала было то, что у меня есть один Singleton объекта ведения журнала в двух загрузчиках классов в одной и той же виртуальной машине (!)

Я прошу прощения за то, что вы не так специфичны для С#, но из того, что я видел, решения с С# выглядят похожими на Java, где у нас был log4j, и мы также должны сделать его одиночным.

Вот почему мне действительно понравилось решение с GAE/python, это так просто, как может быть, и вам не нужно беспокоиться о загрузчики классов, получение отчета о двойном протоколировании или любой шаблон дизайна вообще.

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

Ответ 8

Другим решением, которое я лично считаю самым простым, является использование статического класса Logger. Вы можете вызвать его из любого метода класса без необходимости изменения класса, например. добавьте вложение свойств и т.д. Его довольно простой и простой в использовании.

Logger::initialize ("filename.log", Logger::LEVEL_ERROR); // only need to be called once in your application

Logger::log ("my error message", Logger::LEVEL_ERROR); // to be used in every method where needed

Ответ 9

Учитывая, что в наши дни я использую общий логгер (ILogger для типа T) - ответ нет;)