У меня была привычка передавать логгер конструктору, например:
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");
ОБНОВЛЕНИЕ: Как правильно заметил Мерлин, я должен упомянуть, что в первом и втором примерах я использую DI.
Ответы:
Это правда. Но вы можете сделать лишь так много для решения сквозной проблемы, которая пронизывает каждый ваш тип. Вы должны использовать регистратор везде, поэтому у вас должно быть свойство для этих типов.
Итак, давайте посмотрим, что мы можем с этим поделать.
Синглтон
Синглтоны ужасны
<flame-suit-on>
.Я рекомендую придерживаться инъекции свойств, как вы это сделали со вторым примером. Это лучший факторинг, который вы можете сделать, не прибегая к магии. Лучше иметь явную зависимость, чем скрывать ее через синглтон.
Но если синглтоны сэкономят вам значительное время, включая весь рефакторинг, который вам когда-либо придется делать (время хрустального шара!), Я полагаю, вы сможете с ними жить. Если когда-либо был использован синглтон, то это могло быть оно. Имейте в виду, что цена, если вы когда-нибудь захотите передумать, будет максимально высокой.
Если вы сделаете это, проверьте ответы других людей , используя в
Registry
шаблон (см описание), и те , регистрируя (самовосстанавливающийся) одноплодной завод , а не экземпляр одноплодной регистратор.Есть и другие альтернативы, которые могут работать так же хорошо без особых компромиссов, поэтому вам следует сначала проверить их.
Фрагменты кода Visual Studio
Вы можете использовать фрагменты кода Visual Studio, чтобы ускорить ввод этого повторяющегося кода. Вы сможете ввести что-то вроде
logger
tab , и код волшебным образом появится для вас.Использование 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
источник
</flame-suit-on>
Не знаю, как вы жили в огненном костюме 5 лет, но надеюсь, что это поможет.Я помещаю экземпляр регистратора в свой контейнер для внедрения зависимостей, который затем вводит регистратор в классы, которые в нем нуждаются.
источник
Хороший вопрос. Я считаю, что в большинстве проектов логгер является синглтоном.
Мне приходят в голову некоторые идеи:
Object
типа , так что каждый класс будет иметь возможность вызывать методы лесоруба какLogInfo()
,LogDebug()
,LogError()
источник
public static void LogInfo(this Object instance, string message)
чтобы каждый класс их подбирал , Что касаетсяServiceLocator
- это позволяет вам иметь регистратор в качестве обычного экземпляра класса, а не синглтона, поэтому вы дадите большую гибкостьСинглтон - хорошая идея. Еще лучшая идея - использовать шаблон реестра , который дает немного больше контроля над созданием экземпляров. На мой взгляд, шаблон singleton слишком близок к глобальным переменным. При создании или повторном использовании объекта, обрабатывающего реестр, есть место для будущих изменений правил создания экземпляров.
Сам реестр может быть статическим классом, чтобы предоставить простой синтаксис для доступа к журналу:
Registry.Logger.Info("blabla");
источник
Простой синглтон - не лучшая идея. Это затрудняет замену регистратора. Я обычно использую фильтры для своих регистраторов (некоторые «шумные» классы могут регистрировать только предупреждения / ошибки).
Я использую шаблон singleton в сочетании с шаблоном прокси для фабрики логгеров:
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
без изменения кода (и, следовательно, в соответствии с принципом открытия / закрытия).Пример расширения
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"); } }
И использовать новый завод
// 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!"); } }
источник
Instance
в корне приложения, а я не знаю почему вы сбросили его)Есть книга Dependency Injection in .NET. Исходя из того, что вам нужно, вы должны использовать перехват.
В этой книге есть диаграмма, помогающая решить, использовать ли внедрение конструктора, внедрение свойств, внедрение методов, окружающий контекст, перехват.
Вот как можно рассуждать, используя эту диаграмму:
Использовать перехват
источник
Другое решение, которое я лично считаю самым простым, - использовать статический класс 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
источник
Если вы хотите найти хорошее решение для ведения журнала, я предлагаю вам взглянуть на движок приложений Google с python, где ведение журнала так же просто, как
import logging
и тогда вы можете простоlogging.debug("my message")
илиlogging.info("my message")
который действительно делает его настолько простым, насколько и должно.У Java не было хорошего решения для ведения журнала, т.е. следует избегать log4j, поскольку он практически вынуждает вас использовать синглтоны, которые, как здесь ответили, «ужасны», и у меня был ужасный опыт попытки сделать вывод журнала одним и тем же оператором ведения журнала только один раз когда я подозреваю, что причиной двойного ведения журнала было то, что у меня есть один синглтон объекта ведения журнала в двух загрузчиках классов на одной виртуальной машине (!)
Прошу прощения за то, что я не настолько специфичен для C #, но из того, что я видел, решения с C # выглядят похожими на Java, где у нас был log4j, и мы также должны сделать его синглтоном.
Вот почему мне очень понравилось решение с GAE / python , оно настолько простое, насколько это возможно, и вам не нужно беспокоиться о загрузчиках классов, получении оператора двойной регистрации или каких-либо шаблонов проектирования вообще в этом отношении.
Я надеюсь, что часть этой информации может иметь отношение к вам, и я надеюсь, что вы захотите взглянуть на решение для ведения журнала, которое я рекомендую вместо этого, я запугиваю, насколько проблема синглтона подозревается из-за невозможности иметь настоящий синглтон, когда он должен быть запущен в нескольких загрузчиках классов.
источник
using Logging; /* ... */ Logging.Info("my message");
logging.info("my message")
программу более сложную, чем hello world. Обычно вы делаете довольно много шаблонной инициализации регистратора - настраивая форматтер, уровень, настраивая обработчики файлов и консоли. Никогдаlogging.info("my message")
!