.net 如何为 WCF 服务创建全局异常处理程序?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/747011/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I create a global exception handler for a WCF Services?
提问by Thomas Jespersen
I want to log all exceptions server side.
我想记录服务器端的所有异常。
In ASP.NET I write something like this in Global.asax.cs, but will this work for a WCF service, too?
在 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
...
}
}
UPDATE: I don't want to have a try...catch for every [OperationContract] in my .svc file. I short... I want to make sure that all exception that my service throws is logged by log4net. I'm not talking about how the client handles exception.
更新:我不想对 .svc 文件中的每个 [OperationContract] 进行 try...catch。我简短......我想确保我的服务抛出的所有异常都由 log4net 记录。我不是在谈论客户端如何处理异常。
采纳答案by Marc Gravell
You can create a WCF error-logger by implementing IErrorHandlerand associating it with the service; typically (for logging) you would return falsefrom HandleError(allowing other handlers to execute), and log the error either in HandleError(using the Exception) or in ProvideFault(using the ref Message fault).
您可以通过实现IErrorHandler并将其与服务相关联来创建 WCF 错误记录器;通常(用于日志记录)您false将从HandleError(允许其他处理程序执行)返回,并在HandleError(使用Exception)或ProvideFault(使用ref Message fault)中记录错误。
I apply this handler by writing a custom behavior (inheriting from BehaviorBase), which (in ApplyDispatchBehavior) adds the error-handler to endpointDispatcher.ChannelDispatcher.ErrorHandlersif it isn't already there.
我通过编写自定义行为(继承自BehaviorBase)来应用此处理ApplyDispatchBehavior程序,endpointDispatcher.ChannelDispatcher.ErrorHandlers如果错误处理程序尚不存在,该行为(在 中)将添加到该处理程序中。
The behavior can be applied via configuration.
该行为可以通过配置应用。
回答by Marcel
This is not an answer itself, but an addendum to the great answerprovided by SkyBlade002, providing the given code in C#, for convenience for any C# coder:
这不是一个答案本身,而是一个增编伟大的答案被SkyBlade002提供,提供给定的代码在C#中,为了方便对任何C#编码器:
Here's my code (in ErrorHandler.cs):
这是我的代码(在 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;
}
Here's my code (in ErrorHandlerBehavior.cs):
这是我的代码(在 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();
}
}
Here's my code (in ErrorServiceBehavior.cs):
这是我的代码(在 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);
}
}
}
回答by SkyBlade002
Ran into this issue myself and after many open tabs of research, including articles linked in other answers, here is exactly what someone needs for this.
我自己遇到了这个问题,经过许多开放的研究标签,包括其他答案中链接的文章,这正是有人需要的。
Here is the ErrorHandler class you will need...
这是您需要的 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
Then, these sections need to be added/updated in the App.config file for your service project...
然后,需要在您的服务项目的 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>
Specifically, a behaviorExtension needs to be added, its name needs to be added to the behavior section, the behavior section needs to be named, and that behavior section needs to be the behaviorConfiguration for the service.
具体来说,需要添加一个behaviorExtension,它的名字需要添加到行为部分,行为部分需要命名,行为部分需要是服务的behaviorConfiguration。
Be careful with the "type" attribute on the behaviorExtension though, it must be exact. If you are unsure you can use the following to determine it GetType(ErrorHandlerBehavior).AssemblyQualifiedName.
但是请注意 behaviorExtension 上的“type”属性,它必须是准确的。如果您不确定,可以使用以下方法来确定GetType(ErrorHandlerBehavior).AssemblyQualifiedName。
If you are interested, this is the code behind the logEvent function I use...
如果您有兴趣,这是我使用的 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

