WCF, IErrorHandler и log4Net - PullRequest
       13

WCF, IErrorHandler и log4Net

1 голос
/ 24 января 2011

Я пытаюсь реализовать поведение обработки ошибок в сервисе wcf, который будет использовать log4net для регистрации исключения

[AttributeUsage(AttributeTargets.Class)]
public class AErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior, IErrorHandler{

  private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  protected Type ServiceType { get; set; }
  public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
   //Dont do anything
  }

  public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection <ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
  {
   //dont do anything
  }

  public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
  {
   ServiceType = serviceDescription.ServiceType;
   foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
   {
    dispatcher.ErrorHandlers.Add(this);
   }
  }

  public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
  {
   fault = null; //Suppress any faults in contract
  }

  public bool HandleError(Exception error)
  {   
   log.Error("Page Load failed : " + error.Message); 
   return false;
  }
}

Затем я реализую сервис, который использует поведение. Это прекрасно работает, если я объявил переменную ILog в сервисе

[AErrorHandlerBehavior]
public class AService : IAService
{

     private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        ....//various methods
}

Однако, когда переменная ILog не объявлена, запись перестает работать.

[AErrorHandlerBehavior]
public class AService : IAService
{

     //private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        ....//various methods
}

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

Может кто-нибудь объяснить, а) почему это должно быть объявлено как в поведении, так и в сервисе. и б) любой способ избежать двойного объявления или в) лучший способ входа в wcf.

Ответы [ 2 ]

2 голосов
/ 25 января 2011

Я не уверен в коде, который вы используете для разрешения этого объекта журнала:

private static readonly ILog log = 
  LogManager.GetLogger(
    System.Reflection.MethodBase.GetCurrentMethod().DeclaringType
  );

Это всегда будет разрешать тип, в котором написана строка кода, так как сначала разрешается автоматически определенный статический конструктор (с MethodInfo.GetCurrentMethod() окружающего класса), а затем он получает свой тип объявления.

Ну, я говорю всегда ...

Мне не нравится этот тип паттерна, поскольку он неявно опирается на код, который вы сами не написали физически (и, следовательно, внутреннюю особенность компилятора, которая может изменить в любое время); было бы немного лучше, если бы вы явно поместили его в статический конструктор, который вы пишете сами.

Если вы хотите сделать это, используйте

typeof(_whatever_type_you_declare_it_in_);

... не полагайтесь на компилятор и среду выполнения.

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

Планируете ли вы иметь возможность управлять журналом, который будет использоваться на уровне обслуживания, с уровнем поведения по умолчанию?

Если это так, то я предлагаю вам:

1) Добавьте к своему поведению свойство ILog уровня экземпляра, которое называется _serviceLog

2) В вашей реализации ApplyDispatchBehaviour сделайте это:

_serviceLog = LogManager.GetLogger(serviceDescription.ServiceType);

3) И тогда ваша реализация HandleError может выглядеть следующим образом:

public bool HandleError(Exception error)
{
  //use the service-level log, or a default
  ILog targetLog = _serviceLog ?? log;
  if(targetLog != null)
    targetLog.Error("Page Load failed : " + error.Message);
  return false;
}
0 голосов
/ 25 января 2011

Реальная проблема, игнорируя то, как правильно получить регистратор, заключалась в том, что в поведении я не настроил регистратор.

Исправление заключалось в простом добавлении оператора Configure в ApplyDispatchBehavior

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            XmlConfigurator.Configure();            
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(this);
            }
        }

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

...