【问题标题】:How do I create a global exception handler for a WCF Services?如何为 WCF 服务创建全局异常处理程序?
【发布时间】:2010-10-19 07:42:44
【问题描述】:

我想记录所有异常服务器端

在 ASP.NET 中,我在 Global.asax.cs 中编写了类似的内容,但这也适用于 WCF 服务吗?

public class Global : HttpApplication
{
    protected void Application_Error(object sender, EventArgs e)
    {
        Exception unhandledException = Server.GetLastError();

        //Log exception here
        ...
    }
} 

更新:我不想为我的 .svc 文件中的每个 [OperationContract] 尝试...catch。我简短...我想确保我的服务抛出的所有异常都由 log4net 记录。我不是在谈论客户端如何处理异常。

【问题讨论】:

    标签: .net wcf


    【解决方案1】:

    这本身不是答案,而是SkyBlade002 提供的great answer 的附录,提供了C# 中的给定代码,以方便任何C# 编码人员:

    这是我的代码(在 ErrorHandler.cs 中):

    /// <summary>
    ///     This class defines a global ErrorHandler, that allows us to control the fault message returned to the client and
    ///     perform custom error processing like logging.
    /// </summary>
    public class ErrorHandler : IErrorHandler {
        private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
        /// <summary>
        ///     Enables the creation of a custom <see cref="T:System.ServiceModel.FaultException`1" /> that is returned from an
        ///     exception in the course of a service method.
        /// </summary>
        /// <remarks>
        ///     This method is optionally used to transform standard exceptions into custom FaultException(Of TDetail) that
        ///     can be passed back to the service client.
        /// </remarks>
        /// <param name="error">The <see cref="T:System.Exception" /> object thrown in the course of the service operation.</param>
        /// <param name="version">The SOAP version of the message.</param>
        /// <param name="fault">
        ///     The <see cref="T:System.ServiceModel.Channels.Message" /> object that is returned to the client, or
        ///     service, in the duplex case.
        /// </param>
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault) {}
    
        /// <summary>
        ///     This method will be called whenever an exception occurs. Therefore,
        ///     we log it and then return false so the error can continue to propagate up the chain.
        /// </summary>
        /// <param name="error">Exception being raised.</param>
        /// <returns>False to let the error propagate up the chain, or True to stop the error here.</returns>
        public bool HandleError(Exception ex) {
            Log.Error(ex); //TODO your own logging
            return false;
        }
    

    这是我的代码(在 ErrorHandlerBehavior.cs 中):

    /// <summary>
    ///     This class defines a BehaviorExtensionElement, so that we can use the ErrorServiceBehavior class, defined above, in
    ///     our App.config.
    /// </summary>
    public class ErrorHandlerBehavior : BehaviorExtensionElement {
        /// <summary>
        ///     Gets the type of behavior.
        /// </summary>
        public override Type BehaviorType {
            get { return typeof (ErrorServiceBehavior); }
        }
    
        /// <summary>
        ///     Creates a behavior extension based on the current configuration settings.
        /// </summary>
        /// <returns>
        ///     The behavior extension.
        /// </returns>
        protected override object CreateBehavior() {
            return new ErrorServiceBehavior();
        }
    }
    

    这是我的代码(在 ErrorServiceBehavior.cs 中):

    /// <summary>
    ///     This class defines a ServiceBehavior, that will allow us to add our custom ErrorHandler class, defined above, to
    ///     each channel we have a service running on.
    /// </summary>
    public class ErrorServiceBehavior : IServiceBehavior {
        /// <summary>
        ///     Provides the ability to inspect the service host and the service description to confirm that the service can run
        ///     successfully.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The service host that is currently being constructed.</param>
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {}
    
        /// <summary>
        ///     Provides the ability to pass custom data to binding elements to support the contract implementation.
        /// </summary>
        /// <param name="serviceDescription">The service description of the service.</param>
        /// <param name="serviceHostBase">The host of the service.</param>
        /// <param name="endpoints">The service endpoints.</param>
        /// <param name="bindingParameters">Custom objects to which binding elements have access.</param>
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) {}
    
        /// <summary>
        ///     Provides the ability to change run-time property values or insert custom extension objects such as error handlers,
        ///     message or parameter interceptors, security extensions, and other custom extension objects.
        /// </summary>
        /// <param name="serviceDescription">The service description.</param>
        /// <param name="serviceHostBase">The host that is currently being built.</param>
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) {
            //Enumerate all channels and add the error handler to the collection
            var handler = new ErrorHandler();
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers) {
                dispatcher.ErrorHandlers.Add(handler);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我自己遇到了这个问题,经过许多打开的研究标签,包括在其他答案中链接的文章,这正是有人需要的。

      这是您需要的 ErrorHandler 类...

      Imports System.ServiceModel.Configuration
      Imports System.ServiceModel.Description
      Imports System.ServiceModel.Dispatcher
      
      ''' <summary>
      ''' This class defines a global ErrorHandler, that allows us to control the
      ''' fault message returned to the client and perform custom error processing
      ''' like logging.
      ''' </summary>
      Public Class ErrorHandler
          Implements IErrorHandler
      
          ''' <summary>
          ''' This method will be called whenever an exception occurs. Therefore,
          ''' we log it and then return false so the error can continue to propagate up the chain.
          ''' </summary>
          ''' <param name="ex">Exception being raised.</param>
          ''' <returns>False to let the error propagate up the chain, or True to stop the error here.</returns>
          Public Function HandleError(ByVal ex As Exception) As Boolean Implements IErrorHandler.HandleError
              'Unknown error occurred at the Service layer, log the event
              logEvent("Encountered an unknown error at the Service layer.", ex, EventLogEntryType.Error)
      
              Return False
          End Function
      
          ''' <summary>
          ''' This method is optionally used to transform standard exceptions into custom
          ''' FaultException(Of TDetail) that can be passed back to the service client.
          ''' </summary>
          ''' <param name="ex">Exception being raised.</param>
          ''' <param name="version">SOAP version of the message.</param>
          ''' <param name="fault">Message object that is returned to the client.</param>
          Public Sub ProvideFault(ByVal ex As Exception, ByVal version As Channels.MessageVersion, ByRef fault As Channels.Message) Implements IErrorHandler.ProvideFault
          End Sub
      
      End Class
      
      ''' <summary>
      ''' This class defines a ServiceBehavior, that will allow us to add our
      ''' custom ErrorHandler class, defined above, to each channel we have a
      ''' service running on.
      ''' </summary>
      Public Class ErrorServiceBehavior
          Implements IServiceBehavior
      
          Public Sub AddBindingParameters(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase, endpoints As ObjectModel.Collection(Of ServiceEndpoint), bindingParameters As Channels.BindingParameterCollection) Implements IServiceBehavior.AddBindingParameters
          End Sub
      
          Public Sub ApplyDispatchBehavior(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.ApplyDispatchBehavior
              'Enumerate all channels and add the error handler to the collection
              Dim handler As New ErrorHandler()
              For Each dispatcher As ChannelDispatcher In serviceHostBase.ChannelDispatchers
                  dispatcher.ErrorHandlers.Add(handler)
              Next
          End Sub
      
          Public Sub Validate(serviceDescription As ServiceDescription, serviceHostBase As ServiceHostBase) Implements IServiceBehavior.Validate
          End Sub
      
      End Class
      
      ''' <summary>
      ''' This class defines a BehaviorExtensionElement, so that we can
      ''' use the ErrorServiceBehavior class, defined above, in our App.config.
      ''' </summary>
      Public Class ErrorHandlerBehavior
          Inherits BehaviorExtensionElement
      
          Protected Overrides Function CreateBehavior() As Object
              Return New ErrorServiceBehavior()
          End Function
      
          Public Overrides ReadOnly Property BehaviorType As Type
              Get
                  Return GetType(ErrorServiceBehavior)
              End Get
          End Property
      
      End Class
      

      然后,需要在您的服务项目的 App.config 文件中添加/更新这些部分...

      <system.serviceModel>
        <extensions>
          <behaviorExtensions>
            <!-- Add in our custom error handler -->
            <add name="ErrorLogging" type="Service.ErrorHandlerBehavior, Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          </behaviorExtensions>
        </extensions>
        <services>
          <service name="Service.Service" behaviorConfiguration="Service.ServiceBehavior">
            <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttp"
            contract="SentinelService.IService"/>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="Service.ServiceBehavior">
              <serviceMetadata httpGetEnabled="True" />
              <serviceDebug includeExceptionDetailInFaults="False" />
              <!-- Add in our custom error handler, from behaviorExtensions element -->
              <ErrorLogging />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      

      具体来说,需要添加一个behaviorExtension,它的名称需要添加到行为部分,行为部分需要命名,并且该行为部分需要是服务的行为配置。

      但要小心 behaviorExtension 上的“type”属性,它必须是准确的。如果你不确定你可以使用下面的方法来确定它GetType(ErrorHandlerBehavior).AssemblyQualifiedName

      如果你有兴趣,这是我使用的 logEvent 函数背后的代码...

      ''' <summary>
      ''' Logs a message and optional exception to the application event log.
      ''' </summary>
      ''' <param name="message">String message to log.</param>
      ''' <param name="ex">Exception to log.</param>
      ''' <param name="eventType">EventLogEntryType indicating the message severity.</param>
      Public Sub logEvent(ByVal message As String, Optional ByVal ex As Exception = Nothing, _
                          Optional ByVal eventType As EventLogEntryType = EventLogEntryType.Information)
          'Initialize report
          Dim report As String = message + vbNewLine + vbNewLine
      
          'Create eventLogger
          Dim eventLogger As New EventLog()
      
          'Register event source, add any Exception information to the report, and then log it
          Try
              'Register the app as an Event Source
              If Not EventLog.SourceExists("MyAppName") Then
                  EventLog.CreateEventSource("MyAppName", "Application")
              End If
      
              If ex IsNot Nothing Then
                  'Force eventType to error
                  eventType = EventLogEntryType.Error
      
                  'Add Exception Information to report
                  report += "Exception Information:" + vbNewLine
                  Dim currentException As Exception = ex
                  Dim exCount As Integer = 1
                  While (currentException IsNot Nothing)
                      report += Space(5) + If(exCount = 1, "Message:", "Inner Exception:") + vbNewLine
                      report += Space(10) + currentException.Message + vbNewLine
                      report += Space(5) + "StackTrace:" + vbNewLine
                      report += Space(10) + currentException.StackTrace + vbNewLine
                      report += vbNewLine
      
                      currentException = currentException.InnerException
                      exCount += 1
                  End While
              End If
          Catch reportException As Exception
              'This catch ensures that no matter what some error report is logged.
              report += vbNewLine + vbNewLine + "PARTIAL REPORT!!!...AN ERROR WAS ENCOUNTERED GENERATING ERROR REPORT."
          Finally
              Try
                  'Log report
                  eventLogger.Source = "MyAppName"
                  eventLogger.WriteEntry(report, eventType)
              Catch logEventException As Exception
                  'Eat it...nothing can be done at this point and we do
                  'not want the application to crash over an error report
                  'if we can prevent it
              End Try
          End Try
      End Sub
      

      【讨论】:

      • 我在 web.config 中收到关于“元素 'behavior' 具有无效的子元素 'MyBehaviorName' 的警告。可能的列表......”有没有办法让这个警告消失?谢谢你的例子。
      • 这是最完整的答案。它以清晰的方式包含所需的一切。
      • Up 1 用于在 VB.Net 中提供答案。 =) 它也很简洁。
      【解决方案3】:

      您可以通过实现IErrorHandler 并将其与服务关联来创建WCF 错误记录器;通常(用于记录)您将从HandleError(允许其他处理程序执行)返回false,并将错误记录在HandleError(使用Exception)或ProvideFault(使用ref Message fault )。

      我通过编写自定义行为(从BehaviorBase 继承)来应用此处理程序,如果错误处理程序不存在,它(在ApplyDispatchBehavior 中)会将错误处理程序添加到endpointDispatcher.ChannelDispatcher.ErrorHandlers

      可以通过配置应用该行为。

      【讨论】:

      • 好链接;如果可以的话,我会 +1 该链接
      • @LukasCenovsky +1 只包含精华的链接。
      • Marc,它会捕获所有异常吗?它把我扔到 CurrentDomain.UnhandledException 中。我该怎么办?它使我的服务器应用程序崩溃。 (使用上下文单个,并发多个) System.ServiceModel.CommunicationObjectFaultedException:通信对象 System.ServiceModel.Channels.SecurityChannelListener`1+SecurityReplySessionChannel[System.ServiceModel.Channels.IReplySessionChannel] 不能用于通信,因为它在故障状态。在 System.ServiceModel.Channels.CommunicationObject.ThrowIfFaulted()
      • @MarcGravell:请回答我的问题stackoverflow.com/questions/26149017/…
      猜你喜欢
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 2020-02-05
      • 1970-01-01
      • 2013-03-18
      • 2014-04-17
      • 2012-12-31
      • 2011-05-19
      相关资源
      最近更新 更多