#dependency-injection #log4net #structuremap
#внедрение зависимостей #log4net #structuremap
Вопрос:
Я использую Log4Net как сервис, который внедряется в другие сервисы с помощью StructureMap.
Как мне убедиться, что файл журнала включает контекст вызывающего класса сервиса (имя класса и / или поток), который выполняет вызовы log4net?
Конечно, вызывающий класс или поток всегда будет службой ведения журнала, что не помогает мне понять, откуда на самом деле поступают вызовы ведения журнала.
Редактировать:
Код регистрации:
ObjectFactory.Initialize(x =>
{
x.For<ILog>().AlwaysUnique().Use(s => s.ParentType == null ?
LogManager.GetLogger(s.BuildStack.Current.ConcreteType) :
LogManager.GetLogger(s.ParentType));
});
Уровень сервиса:
public class LoggerService : ILoggerService
{
private readonly ILog log;
public LoggerService(ILog logger)
{
log = logger;
log.Info("Logger started {0}".With(logger.Logger.Name));
}
public void Info(string message)
{
log.Info(message);
}
}
При протоколировании я по-прежнему всегда получаю LoggerService в качестве контекста, поэтому я никогда не увижу, что на самом деле вызвало регистратор. Похоже, что он работает некорректно. Я чувствую, что здесь я чего-то не понимаю…
Правка 2: Я добавил сюда ссылку на вставку для консольного приложения:
Я ожидал бы, что родительский класс будет зарегистрирован, но он не работает на самом простом уровне.
Комментарии:
1. Кажется, вы абстрагировали регистратор до уровня выше того, что я обычно делаю, но попробуйте это для ObjectFactory. Вместо этого инициализируйте: замените [LogManager. getLogger(s.ParentType)] с помощью [LogManager. getLogger(s.ParentType. UnderlyingSystemType.Name ))]
2. @Bryan — Я пробовал использовать s.root. Конкретный тип. Назовите вместо этого и настройте app.config на использование %logger для вывода имени регистратора. Хотя это не дает мне имя регистратора SubActionService (см. Мою ссылку ниже). UnderlyingSystemType всегда LoggerService, что не помогает.
3. Попробуйте использовать точку останова внутри [x.Для<ILog>(). AlwaysUnique(). Используйте] лямбда-выражение, изучите в окне просмотра значение [s.ParentType] и перейдите к значению, которое вы ищете. Это то, что я использовал при использовании вашего исходного кода для просмотра родительского типа внедренного класса.
Ответ №1:
Возможно, вы захотите взглянуть на Castle Dynamic proxy, чтобы решить эту проблему с помощью AOP. В группе Google Structure Map есть пример его использования со Structure Map.
У Ayende есть пример ведения журнала на основе AOP с использованием Log4Net и Windsor.
Комментарии:
1. Спасибо за ссылки. Есть ли какой-либо способ использовать StructureMap, поскольку это то, что мое приложение в настоящее время использует для DI?
2. Да, взгляните на пример, на который я ссылался, используя StructureMap: bit.ly/mmAsN5 (он использует более старый синтаксис для SM, но принцип тот же). По сути, вы используете EnrichWith и оборачиваете экземпляры, которые хотите регистрировать, с помощью DynamicProxy и реализуете перехватчик ведения журнала. Перехватчик в примере регистрируется на консоли, но вы могли бы переключить его на Log4Net.
Ответ №2:
Я использую StructureMap во многих кодах, которые я генерирую, и у меня есть реестр StructureMap, который я использую для подключения регистратора к контексту класса, в который он внедряется.
Для справки, я использую версию StructureMap 2.6.2, но должно подойти и 2.5 , где используется новый формат .For<>().Use<>() .
public class CommonsRegistry : Registry
{
public CommonsRegistry()
{
For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType.UnderlyingSystemType.Name));
XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location), "Log.config")));
}
}
Что делает этот реестр, так это для того, чтобы везде, где внедряется ILogger, использовать класс, в который он внедряется, где регистрируются сообщения журнала / context of.
*Кроме того, во второй строке (XmlConfigurator.ConfigureAndWatch) — это место, где я говорю Log4Net получать информацию о протоколировании из файла «Log.config» вместо файла конфигурации приложения, вам это может понравиться, а может и не понравиться, и его можно опустить.
Код, который я использую, является обычным IOC.Процедура запуска, которую я бы прошел, если бы хотел использовать регистр по умолчанию.
ObjectFactory.Initialize(x =>
{
x.AddRegistry<CommonsRegistry>();
...
}
Это дает мне имя вызывающего класса в экземпляре журнала, в который автоматически регистрируются сообщения, и все, что требуется, это внедрить регистратор в класс.
class foo
{
private readonly ILogger _log;
public foo(ILogger log)
{
_log = log;
}
}
Теперь сообщения регистрируются как контекст / класс «foo».
Комментарии:
1. Привет, как это соотносится с функцией обогащения из первого предложения? Я пытаюсь взвесить плюсы / минусы для обоих вариантов. Хотя выглядит неплохо!
2. В документации enrich говорится «EnrichWith() — Регистрирует функцию, которая выполняется для нового объекта после создания и дает вам возможность возвращать объект, отличный от исходного объекта», где моя функция реестра находится во время создания регистратора, установите соответствующий контекст (родительский класс foo в моем примере).
3. @jaffa — как говорит @Bryan, EnrichWith заменяет объект новым объектом. В случае ведения журнала с использованием DynamicProxy вы бы создали класс-оболочку вокруг целевого объекта, который выполняет ведение журнала. Преимущество использования AOP заключается в том, что вам не нужно добавлять код ведения журнала в каждое место, куда вы хотите войти, он будет перехвачен во время выполнения и автоматически зарегистрирован. Смотрите сообщение Ayendes для некоторого дальнейшего объяснения.
4. Обновлена процедура CommonsRegistry.