ELMAH를 사용하는 WCF 서비스에 대한 예외 로깅
우리는 ASP.NET 3.5 웹 응용 프로그램에서 처리되지 않은 예외를 처리하기 위해 우수한 ELMAH 를 사용하고 있습니다. 이것은 REST 기능을 사용하여 소비되는 WCF 서비스를 제외한 모든 사이트에서 매우 잘 작동합니다. 응용 프로그램 코드에서 처리하지 않는 작업 메서드 내에서 예외가 발생하면 WCF는 서비스 계약 및 구성 설정에 따라 다양한 방식으로 처리합니다. 이는 예외로 인해 ELMAH가 사용 하는 ASP.NET HttpApplication.Error 이벤트가 발생하지 않음을 의미 합니다. 내가 알고있는 두 가지 해결책은 다음과 같습니다.
- 모든 메서드 호출을 try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext (). Raise (ex); 던지다; } catch 블록 내에서 Elmah를 명시 적으로 호출합니다.
- Will Hughes의 블로그 게시물 인 Making WCF와 ELMAH가 함께 훌륭하게 작동하도록 IErrorHandler 를 사용 하여 ELMAH에 대한 별도의 ErrorHandler 호출을 제외합니다.
첫 번째 옵션은 매우 간단하지만 정확히 DRY 는 아닙니다 . 두 번째 옵션은 속성과 ErrorHandler를 구현 한 후 사용자 지정 속성으로 각 서비스를 장식하기 만하면됩니다. Will의 작업을 기반으로이 작업을 수행했지만 코드를 게시하기 전에 이것이 올바른 접근 방식 인지 확인하고 싶습니다 .
내가 놓친 더 좋은 방법이 있습니까?
IErrorHandler에 대한 MSDN 문서에 따르면 HandleError 메서드는 로깅을 수행하는 장소이지만 ELMAH 는 HttpContext.Current에 액세스합니다. HttpContext.Current를 사용할 수 있어도이 메서드 내에서 null 인 ApplicationInstance . ProvideFault 메서드 내에서 Elmah를 호출하는 것은 ApplicationInstance가 설정되어 있기 때문에 해결 방법이지만 API 문서에 설명 된 의도와 일치하지 않습니다. 여기에 뭔가 빠졌나요? 문서에는 작업 스레드에서 호출되는 HandleError 메서드에 의존해서는 안된다고 명시되어 있습니다. 이것이이 범위에서 ApplicationInstance가 null 인 이유 일 수 있습니다.
내 블로그 게시물 (OP에서 참조 됨)의 솔루션은 오류 상태 동안 HTTP 응답 코드를 변경하는 데 사용했던 기존 솔루션을 기반으로했습니다.
따라서 우리에게는 예외를 ELMAH에 전달하는 것이 한 줄로 변경되었습니다. 더 나은 해결책이 있다면 저도 알고 싶습니다.
Posterity / Reference 및 잠재적 인 개선을 위해 현재 솔루션의 코드는 다음과 같습니다.
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);
}
}
}
}
사용 예
ServiceErrorBehaviour 속성을 사용하여 WCF 서비스를 장식합니다.
[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
// ...
}
BehaviorExtensionElement를 만들 때 config를 사용하여 동작을 활성화 할 수도 있습니다.
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>
이렇게하면 RIA 서비스와 함께 ELMAH를 사용할 수도 있습니다!
Will의 작업을 기반으로이 작업을 수행했지만 코드를 게시하기 전에 이것이 올바른 접근 방식인지 확인하고 싶습니다.
I think this is a great approach (kudos to Will for this posting!). I don't think Will or you have missed anything here. Implementing IErrorHandler is the preferred way of capturing all possible server-side exceptions that could otherwise cause the communication channel to be faulted (torn down) and thus it's a natural place to hook in some logging like ELMAH.
Marc
This may well be obvious to some people but I just spent quite a while trying to figure out why my HttpContext.Current was null despite following all of Will Hughes' excellent answer. Embarassingly, I realised that this was because my WCF service is activated by a MSMQ message.
I ended up rewriting the ProvideFault()
method:
if (HttpContext.Current == null)
{
ErrorLog.GetDefault(null).Log(new Error(error));
}
else
{
ErrorSignal.FromCurrentContext().Raise(error);
}
I was unable to get the proposed answer working with a WCF Data Service. I wired up the behavior attribute, etc, but still did not get any errors logged. Instead, I ended up adding the following to the service implementation:
protected override void HandleException(HandleExceptionArgs args)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception);
base.HandleException(args);
}
I haven't tried doing this explicitly with the REST stuff, and haven't used ELMAH myself, but another option worth looking into might be to hook into WCF using an IDispatchMessageInspector instead of an IErrorHandler.
참고URL : https://stackoverflow.com/questions/895901/exception-logging-for-wcf-services-using-elmah
'Programing' 카테고리의 다른 글
C 대 C ++ 구조체 정렬 (0) | 2020.11.28 |
---|---|
dotnet cli와 새로운 vs2017 msbuild 간의 관계 (0) | 2020.11.28 |
데이터베이스 호출에 대한 단위 테스트를 작성하는 방법 (0) | 2020.11.28 |
Scala 및 Haskell 유형 시스템의 차이점과 유사점은 무엇입니까? (0) | 2020.11.28 |
8 자 전용 UUID 생성 (0) | 2020.11.28 |