Ведение журнала исключений для служб WCF с использованием ELMAH

Мы используем превосходный ELMAH для обработки необработанных исключений в веб-приложении ASP.NET 3.5. Это очень хорошо работает для всех сайтов, кроме служб WCF, которые используются с использованием функций REST. Когда в методах операции возникает исключение, которое не обрабатывается кодом приложения, WCF обрабатывает его по-разному в зависимости от контрактов службы и параметров конфигурации. Это означает, что исключение не вызывает событие ASP.NET HttpApplication.Error, которое ELMAH использует . Мне известны два решения этой проблемы:

Первый вариант чрезвычайно прост, но не совсем DRY. Второй вариант требует, чтобы вы украсили каждую службу настраиваемым атрибутом только после реализации атрибута и ErrorHandler. Я сделал это на основе работы Уилла, но хочу убедиться, что это правильный подход перед размещением кода.

Есть ли способ получше, который я пропустил?

В документации MSDN для IErrorHandler говорится, что < strong> HandleError - это место для ведения журнала, но ELMAH обращается к HttpContext. Текущий. ApplicationInstance, который в этом методе имеет значение NULL, хотя HttpContext.Current доступен. Вызов Elmah в методе ProvideFault - это обходной путь, поскольку ApplicationInstance установлен, но это не соответствует намерению, описанному в документации API. Я что-то здесь упускаю? В документации указано, что вы не должны полагаться на метод HandleError, вызываемый в потоке операции, что может быть причиной того, что ApplicationInstance имеет значение null в этой области.


person Martin Hollingsworth    schedule 22.05.2009    source источник


Ответы (6)


Решение из моего сообщения в блоге (указанное в OP) было основано на существующем решении, которое мы использовали / используем для изменения кодов ответа HTTP во время состояния ошибки.

Итак, для нас это было однострочное изменение, чтобы передать исключение в ELMAH. Если есть лучшее решение, я тоже хотел бы знать об этом.

Для Posterity / Reference и потенциального улучшения - вот код из текущего решения.

Классы HttpErrorHandler и ServiceErrorBehaviourAttribute

using System;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Net;
using System.Web;
using Elmah;
namespace YourApplication
{
    /// <summary>
    /// Your handler to actually tell ELMAH about the problem.
    /// </summary>
    public class HttpErrorHandler : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            return false;
        }

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            if (error != null ) // Notify ELMAH of the exception.
            {
                if (System.Web.HttpContext.Current == null)
                    return;
                Elmah.ErrorSignal.FromCurrentContext().Raise(error);
            }
        }
    }
    /// <summary>
    /// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
    /// ...and errors reported to ELMAH
    /// </summary>
    public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
    {
        Type errorHandlerType;

        public ServiceErrorBehaviourAttribute(Type errorHandlerType)
        {
            this.errorHandlerType = errorHandlerType;
        }

        public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
        }

        public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
        {
            IErrorHandler errorHandler;
            errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
            foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
            {
                ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }
}

Пример использования

Украсьте свои службы WCF атрибутом ServiceErrorBehaviour:

[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
  // ...
}
person Community    schedule 25.05.2009
comment
Я пробовал это решение в службе WCF и получил эту ошибку :( System.ArgumentNullException: Value не может быть нулевым. Имя параметра: контекст в Elmah.ErrorSignal.FromContext (контекст HttpContext) в c: \ builds \ ELMAH \ src \ Elmah \ ErrorSignal.cs: строка 67 в Elmah.ErrorSignal.FromCurrentContext () в c: \ builds \ ELMAH \ src \ Elmah \ ErrorSignal.cs: строка 61 Помогите, пожалуйста - person IsmailS; 08.06.2010
comment
Похоже, вы не работаете в HttpContext (то есть в ASP.NET) - это означает, что ELMAH вам не подходит. Опубликуйте свою проблему как отдельный вопрос, если вы действительно работаете в ASP.NET - person ; 10.06.2010
comment
Воля! Я работал под HttpContext. По-прежнему не работал. Вот решение, которое я придумал. stackoverflow.com/questions/ 2997076 / - person IsmailS; 28.06.2010
comment
Привет, это не работает для меня, когда служба WCF размещена в IIS с помощью basicHttpBinding, вот мой пост stackoverflow.com/questions/4470362/. пожалуйста помоги. - person RKP; 17.12.2010

При создании BehaviorExtensionElement можно даже активировать поведение с помощью config:

public class ErrorBehaviorExtensionElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(ServiceErrorBehaviourAttribute); }
    }

    protected override object CreateBehavior()
    {
        return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler));
    }
}

Конфиг:

<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <elmah />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>

Таким образом, можно также использовать ELMAH в сочетании с услугами RIA!

person riezebosch    schedule 17.06.2011
comment
Служба WCF RIA является службой WCF. поэтому вы можете применять те же атрибуты в службе RIA, не создавая дополнительных поведений. - person Yeonho; 13.12.2012

Это может быть очевидно для некоторых людей, но я просто потратил довольно много времени, пытаясь выяснить, почему мой HttpContext.Current был нулевым, несмотря на то, что следовал всем превосходным ответам Уилла Хьюза. К сожалению, я понял, что это произошло потому, что моя служба WCF активируется с помощью сообщения MSMQ.

В итоге я переписал метод ProvideFault():

if (HttpContext.Current == null)
{
    ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
    ErrorSignal.FromCurrentContext().Raise(error);
}
person Steve Rukuts    schedule 05.12.2013

Я сделал это на основе работы Уилла, но я хочу убедиться, что это правильный подход, прежде чем размещать код.

Я думаю, что это отличный подход (спасибо Уиллу за эту публикацию!). Не думаю, что Уилл или вы что-то здесь упустили. Реализация IErrorHandler является предпочтительным способом захвата всех возможных исключений на стороне сервера, которые в противном случае могли бы вызвать сбой в канале связи (разрушение), и, таким образом, это естественное место для подключения некоторых журналов, таких как ELMAH.

Марк

person marc_s    schedule 22.05.2009

Мне не удалось получить предложенный ответ при работе со службой данных WCF. Я подключил атрибут поведения и т. Д., Но по-прежнему не регистрировал никаких ошибок. Вместо этого я добавил в реализацию службы следующее:

protected override void HandleException(HandleExceptionArgs args)
{
    Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
    base.HandleException(args);
}
person Daniel    schedule 05.12.2013

Я не пробовал делать это явно с материалом REST и сам не использовал ELMAH, но другой вариант, на который стоит обратить внимание, может заключаться в подключении к WCF с помощью IDispatchMessageInspector вместо IErrorHandler.

person tomasr    schedule 22.05.2009