Log4Net с замком Виндзор
Я настраиваю ведение журнала для своего приложения и для ведения журнала, который я использую log4net
а также castle windsor
для DI.
Я хочу, чтобы структура логирования была включена в пользовательскую реализацию, чтобы ее можно было изменить в будущем.
public interface ICustomLogger
{
void Debug(object message, Exception ex = null);
void Info(object message, Exception ex = null);
void Warn(object message, Exception ex = null);
void Error(object message, Exception ex = null);
void Fatal(object message, Exception ex = null);
}
public class CustomLogger : ICustomLogger
{
private readonly log4net.ILog _log;
private readonly log4net.ILog _log1;
public CustomLogger()
{
//approach1
var stack = new StackTrace();
var frame = stack.GetFrame(1);
var method = frame.GetMethod();
Type type = method.DeclaringType;
_log = log4net.LogManager.GetLogger(type);
//approach2
var dtype = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType;
_log1 = log4net.LogManager.GetLogger(dtype);
}
public CustomLogger(string name)
{
_log = log4net.LogManager.GetLogger(name);
}
public CustomLogger(Type type)
{
_log = log4net.LogManager.GetLogger(type);
}
public void Debug(object message, Exception ex = null)
{
if (_log.IsDebugEnabled)
{
if (ex == null)
{
_log.Debug(message);
}
else
{
_log.Debug(message, ex);
}
}
}
public void Info(object message, Exception ex = null)
{
if (_log.IsInfoEnabled)
{
if (ex == null)
{
_log.Info(message);
}
else
{
_log.Info(message, ex);
}
}
}
public void Warn(object message, Exception ex = null)
{
if (_log.IsWarnEnabled)
{
if (ex == null)
{
_log.Warn(message);
}
else
{
_log.Warn(message, ex);
}
}
}
public void Error(object message, Exception ex = null)
{
if (_log.IsErrorEnabled)
{
if (ex == null)
{
_log.Error(message);
}
else
{
_log.Error(message, ex);
}
}
}
public void Fatal(object message, Exception ex = null)
{
if (_log.IsFatalEnabled)
{
if (ex == null)
{
_log.Fatal(message);
}
else
{
_log.Fatal(message, ex);
}
}
}
}
Чтобы зарегистрировать эту пользовательскую реализацию с DI...
container.Register(Component.For<ICustomLogger>()
.ImplementedBy<CustomLogger>()
.LifeStyle.Transient);
Проблема возникает, когда я прошу DI разрешить регистратор, тогда он всегда возвращает регистратор для Customlogger
введите не тот класс, где я хочу его использовать.
class ABC
{
ICustomLogger _logger;
public ABC(ICustomLogger logger)
{
_logger = logger; // type of this logger is CustomLogger not ABC
}
}
Оба подхода не работают, чтобы разрешить регистратор как ABC. Может кто-нибудь помочь мне понять, что здесь не так и как решить проблему.
2 ответа
Вы можете сделать это с помощью специального средства разрешения зависимостей.
Сначала нужно создать реализацию ISubDependencyResolver
который может разрешать зависимости типа ICustomLogger
:
public class LoggerResolver : ISubDependencyResolver
{
public bool CanResolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
//We can only handle dependencies of type ICustomLogger
return dependency.TargetType == typeof (ICustomLogger);
}
public object Resolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
//We pass the requested type, e.g. ABC, to the constructor of CustomLogger
return new CustomLogger(context.RequestedType);
}
}
Затем вам нужно зарегистрировать этот преобразователь в контейнере следующим образом:
container.Kernel.Resolver.AddSubResolver(new LoggerResolver());
Для вашего конкретного вопроса - в обоих подходах вы никогда не покидаете "рамки" своего класса. С первой вы создаете новую StackTrace, а с другой декларирующим типом конструктора является сам этот класс.
Но вы реализовали конструктор, который может получить тип, так почему бы не использовать его. В настоящее время ваш CustomLogger
зарегистрирован с вашим конструктором по умолчанию:
//There is no place here that you tell castle to resolve using the constructor
//that receives `ABS`
container.Register(Component.For<ICustomLogger>()
.ImplementedBy<CustomLogger>()
.LifeStyle.Transient);
Посмотрите, как Castle Windsor передает параметры конструктора, чтобы понять, как передавать параметры и как вызывать нужный конструктор.
Кроме того - стоит переосмыслить:
Хотя в этом случае хорошей идеей является создание такой абстракции между вашим кодом и внешним источником, я бы этого не делал, и я объясню, почему:
- Из моего опыта никто не меняет структуру логирования после того, как код запущен и работает. Тем более, что вы работаете со зрелой и превосходной основой -
Log4Net
, Он имеет множество встроенных возможностей и очень легко адаптируется для собственных нужд: от различного форматирования сообщений до вывода журналов в разные источники, такие как базы данных, файлы, и, если я не ошибаюсь, есть также приложения для таких вещей, как упругий поиск. - Ты используешь
Castle Windsor
которая имеет хорошую интеграцию сLog4Net
и имеет для вас готовыйLogging Facility
вLog4Net
, Посмотрите этот вопрос, как просто добавить его. - Последний пункт заключается в том, что если вы уже написали хороший код SOLID и передали свой регистратор как
ILogger
для всех компонентов (а не конкретной реализации) все, что они, вероятно, будут делать, это вызывать разныеDebug
/Info
/Warn
/Error
/Fatal
методы - которые будут иметь любая другая зрелая структура ведения журнала. Таким образом, в тот день, когда вам придется изменить (что, я думаю, не произойдет), вы можете написать интерфейс, который выглядит какLog4Net
интерфейс и реализация, которая адаптирует его к вашей новой структуре ведения журналов.