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

StackOverflow https://stackoverflow.com/questions/895901

Вопрос

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

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

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

Документация MSDN для Иеррорхандлер говорит, что HandleError метод — это место для ведения журнала, но ЭЛЬМА обращается к HttpContext.Current.Экземпляр приложения, который в этом методе имеет значение null, хотя HttpContext.Current доступен.Вызов Elmah внутри метода ProvideFault — это обходной путь, поскольку установлен ApplicationInstance, но это не соответствует цели, описанной в документации API. Я что-то упускаю? В документации указано, что вы не должны полагаться на вызов метода HandleError в рабочем потоке, поэтому, возможно, ApplicationInstance имеет значение null в этой области.

Это было полезно?

Решение

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

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

Для потомков/справок и потенциальных улучшений - вот код текущего решения.

Классы 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
{
  // ...
}

Другие советы

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

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!

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

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

Марк

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top