2009-04-14 27 views
53

Tôi muốn đăng nhập tất cả ngoại lệ phía máy chủ.Làm cách nào để tạo trình xử lý ngoại lệ toàn cục cho một Dịch vụ WCF?

Trong ASP.NET tôi viết một cái gì đó như thế này trong Global.asax.cs, nhưng điều này sẽ làm việc cho một dịch vụ WCF, quá?

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

     //Log exception here 
     ... 
    } 
} 

CẬP NHẬT: Tôi không muốn thử ... nắm bắt mọi [OperationContract] trong tệp .svc của tôi. Tôi ngắn ... Tôi muốn chắc chắn rằng tất cả các ngoại lệ mà dịch vụ của tôi ném được đăng nhập bởi log4net. Tôi không nói về cách khách hàng xử lý ngoại lệ.

Trả lời

46

Bạn có thể tạo trình ghi lỗi báo lỗi WCF bằng cách triển khai IErrorHandler và liên kết với dịch vụ; thông thường (để ghi nhật ký), bạn sẽ trả lại false từ HandleError (cho phép các trình xử lý khác thực hiện) và ghi lỗi trong HandleError (sử dụng Exception) hoặc trong ProvideFault (sử dụng ref Message fault).

Tôi áp dụng trình xử lý này bằng cách viết hành vi tùy chỉnh (kế thừa từ BehaviorBase), trong đó (trong ApplyDispatchBehavior) thêm trình xử lý lỗi vào endpointDispatcher.ChannelDispatcher.ErrorHandlers nếu chưa có.

Hành vi có thể được áp dụng thông qua cấu hình.

+0

Liên kết tốt; Tôi muốn +1 liên kết đó nếu tôi có thể –

+1

@LukasCenovsky +1 cho liên kết chỉ chứa bản chất. – pjvds

+0

Marc, nó có bắt được tất cả ngoại lệ không? Nó ném cho tôi điều này trong CurrentDomain.UnhandledException. Tôi nên làm gì về điều đó? Nó làm hỏng ứng dụng máy chủ của tôi. (sử dụng bối cảnh duy nhất, đồng thời nhiều) System.ServiceModel.CommunicationObjectFaultedException: Các đối tượng giao tiếp, System.ServiceModel.Channels.SecurityChannelListener'1 + SecurityReplySessionChannel [System.ServiceModel.Channels.IReplySessionChannel], không thể được sử dụng để giao tiếp vì nó đang ở trạng thái Lỗi. tại System.ServiceModel.Channels.CommunicationObject.ThrowIfFaulted() –

13

Tự mình gặp phải vấn đề này và sau nhiều tab nghiên cứu mở, bao gồm các bài viết được liên kết trong các câu trả lời khác, đây chính là điều mà ai đó cần.

Đây là lớp ErrorHandler bạn sẽ cần ...

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 

Sau đó, các phần này cần phải được thêm/cập nhật trong file App.config cho dự án dịch vụ của bạn ...

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

Cụ thể, một hành viExtension cần phải được thêm vào, tên của nó cần phải được thêm vào phần hành vi, phần hành vi cần được đặt tên và phần hành vi đó cần phải là behaviorConfiguration cho dịch vụ.

Hãy cẩn thận với thuộc tính "loại" trên hành viThay đổi mặc dù, nó phải chính xác. Nếu bạn không chắc chắn, bạn có thể sử dụng các thông tin sau để xác định số GetType(ErrorHandlerBehavior).AssemblyQualifiedName.

Nếu bạn quan tâm, đây là mã đằng sau những chức năng logEvent tôi sử dụng ...

''' <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 
+4

Tôi nhận được cảnh báo trong web.config về "hành vi của phần tử" có phần tử con không hợp lệ 'MyBehaviorName'. Danh sách có thể ... "Có cách nào để lấy cảnh báo này sẽ biến mất? Cảm ơn ví dụ. – crush

+1

Đây là câu trả lời hoàn chỉnh nhất. Nó chứa mọi thứ cần thiết theo cách được giải thích rõ ràng. –

+0

Lên 1 để cung cấp câu trả lời trong VB.Net. =) Nó cũng đẹp và súc tích. –

9

Đây không phải là một câu trả lời riêng của mình, nhưng một phụ lục cho câu trả lời lớn được cung cấp bởi SkyBlade002, cung cấp mã đưa ra trong C#, để thuận tiện cho bất kỳ C# coder:

đây là mã của tôi (trong 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 error) { 
     Log.Error(ex); //TODO your own logging 
     return false; 
    } 

Đây là mã của tôi (trong 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(); 
    } 
} 

Đây là mã của tôi (trong 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); 
     } 
    } 
} 
Các vấn đề liên quan