asp.net wcf exception logging elmah

asp.net - Registro de excepciones para WCF Services utilizando ELMAH



exception logging (6)

He hecho esto basado en el trabajo de Will, pero quiero verificar que este sea el enfoque correcto antes de publicar el código.

Creo que este es un gran enfoque (felicitaciones a Will por esta publicación!). No creo que Will o te hayas perdido algo aquí. La implementación de IErrorHandler es la forma preferida de capturar todas las posibles excepciones del lado del servidor que de lo contrario podrían causar una falla en el canal de comunicación (derribado) y, por lo tanto, es un lugar natural para conectar algunos registros como ELMAH.

Bagazo

Estamos utilizando el excelente ELMAH para tratar excepciones no controladas en una aplicación web ASP.NET 3.5. Esto funciona extremadamente bien para todo el sitio, aparte de los servicios WCF que se consumen utilizando las funciones REST. Cuando se produce una excepción dentro de los métodos de operación que no maneja el código de la aplicación, WCF lo maneja de varias maneras según los contratos de servicio y las configuraciones. Esto significa que la excepción no termina activando el evento ASP.NET HttpApplication.Error que utiliza ELMAH . Las dos soluciones que conozco para tratar con esto son:

  • Ajustar todas las llamadas al método en try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext (). Raise (ex); lanzar; } para llamar explícitamente a Elmah dentro del bloque catch.
  • Use IErrorHandler como se describe en la publicación de blog de Will Hughes. Haciendo que WCF y ELMAH funcionen bien juntos para factorizar la llamada a ELMAH a un ErrorHandler por separado.

La primera opción es extremadamente simple pero no es exactamente DRY . La segunda opción solo requiere que decore cada servicio con el atributo personalizado después de implementar el atributo y el ErrorHandler. He hecho esto basado en el trabajo de Will, pero quiero verificar que este sea el enfoque correcto antes de publicar el código.

¿Hay una mejor manera que me he perdido?

La documentación de MSDN para IErrorHandler dice que el método HandleError es el lugar para hacer el registro, pero ELMAH accede al HttpContext.Current. ApplicationInstance , que es nulo dentro de este método aunque HttpContext.Current está disponible. Realizar la llamada a Elmah dentro del método ProvideFault es una solución alternativa ya que ApplicationInstance está configurado, pero no coincide con el intento descrito en la documentación de la API. ¿Me estoy perdiendo de algo? La documentación indica que no debe confiar en el método HandleError al que se llama en el subproceso de operación, que puede ser el motivo por el que ApplicationInstance es nulo en este ámbito.


Al crear un BehaviorExtensionElement, incluso es posible activar el comportamiento usando config:

public class ErrorBehaviorExtensionElement : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof(ServiceErrorBehaviourAttribute); } } protected override object CreateBehavior() { return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler)); } }

Config:

<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>

¡De esta forma también es posible usar ELMAH en combinación con los servicios de RIA!


Esto puede ser obvio para algunas personas, pero acabo de pasar un buen rato tratando de descubrir por qué mi HttpContext.Current fue nulo a pesar de seguir toda la excelente respuesta de Will Hughes. De manera embarazosa, me di cuenta de que esto se debía a que mi servicio WCF se activa con un mensaje MSMQ.

Terminé reescribiendo el método ProvideFault() :

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


La solución de la publicación de mi blog (a la que se hace referencia en el PO) se basó en una solución existente que estábamos usando para alterar los códigos de respuesta HTTP durante un estado de error.

Entonces, para nosotros fue un cambio de una línea pasar la Excepción a ELMAH. Si hay una solución mejor, me encantaría saberlo también.

Para posteridad / referencia, y mejora potencial: aquí está el código de la solución actual.

HttpErrorHandler y ServiceErrorBehaviourAttribute Classes

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); } } } }

Ejemplo de uso

Decore sus Servicios WCF con el atributo ServiceErrorBehaviour:

[ServiceContract(Namespace = "http://example.com/api/v1.0/")] [ServiceErrorBehaviour(typeof(HttpErrorHandler))] public class MyServiceService { // ... }


No he intentado hacer esto explícitamente con el material REST, y tampoco he usado ELMAH, pero otra opción que vale la pena analizar es enlazar a WCF utilizando un IDispatchMessageInspector lugar de un IErrorHandler.


No pude obtener la respuesta propuesta trabajando con un Servicio de datos de WCF. Conecté el atributo de comportamiento, etc., pero todavía no obtuve ningún error registrado. En cambio, terminé agregando lo siguiente a la implementación del servicio:

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