wcf 上的详细异常日志记录
本文关键字:异常 日志 记录 wcf | 更新日期: 2023-09-27 18:00:48
>最近我正在研究WCF服务的异常日志记录模块。不幸的是,该服务尚未随单元测试一起引入,因此发生了许多意外的异常。到目前为止,我已经通过实现IErrorHandler
接口并将其绑定到具有IServiceBehaviour
的服务接口,完成了使用拦截器aproach获取异常。实际上,我非常喜欢此功能。但它让我进入了下一步的愿望,即获得异常的细节。比如异常发生在哪一行?
我可以通过两种方式满足这种愿望:
- 通过有一个变量来跟踪我成功通过的行,并将其包含在抛出的异常中。
- 通过单独捕获所有行的异常。
但这两种方法对我来说似乎都很糟糕。我想知道是否有已知的设计模式或工具来实现此目标?
在我看来,你可以尝试使用日志记录,比如log4net。然后你可以找出在哪里以及发生了什么。异常对象并不总是包含堆栈信息,因为在优化等过程中发生的"内联"。
包含服务的 PDB 文件,行号将包含在exception.ToString()
我们解决这个问题的方法有两个:
- 我们的服务是命令的愚蠢包装器。因此,当输入服务方法时,它会将其工作委托给命令。
- 我们将每个命令调用包装在负责记录输入、输出和错误并执行命令的日志记录代理中。
例如:
public FooServiceModel GetFoo(int fooId)
{
return new ILogged<GetFooCommand>().Target.Execute(fooId);
}
这会将命令的执行委托给ILogged
,其中:
- 记录命令名称
- 记录命令参数
- 记录执行结果
- 记录任何异常
它还执行一些其他操作,使用自定义消息标头将客户端请求与服务器调用链接起来,以便可以从客户端到服务器并返回完全调试调用。这非常有用,使我们能够在异地诊断甚至复杂的问题。
我们使用Castle.Core
动态代理来实现ILogged
,拦截器看起来像这样(ILog
是一个log4net记录器(:
public class LoggingInterceptor : IInterceptor
{
public LoggingInterceptor([NotNull] object target, [NotNull] ILog logger)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
if (logger == null)
{
throw new ArgumentNullException("logger");
}
this.Target = target;
this.Logger = logger;
}
public object Target { get; set; }
public ILog Logger { get; set; }
public void Intercept(IInvocation invocation)
{
try
{
this.Logger.Debug(invocation);
invocation.ReturnValue = invocation.Method.Invoke(
this.Target, invocation.Arguments);
this.Logger.Debug("Invocation return value:");
this.Logger.Debug(invocation.ReturnValue);
}
catch (TargetInvocationException ex)
{
this.Logger.Error("Unable to execute invocation", ex);
if (ex.InnerException != null)
{
throw ex.InnerException;
}
throw;
}
}
}
调用本身由自定义 log4net 对象渲染器呈现:
public class InvocationRenderer : IObjectRenderer
{
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
{
var invocation = (IInvocation)obj;
var builder = new StringBuilder();
builder.AppendFormat(
"Invoking Method: {0} --> '{1}' with parameters (",
invocation.Method.DeclaringType != null
? invocation.Method.DeclaringType.FullName : "{Unknown Type}",
invocation.Method);
var parameters = invocation.Method
.GetParameters()
.Zip(invocation.Arguments, (p, a) => new { Parameter = p, Argument = a })
.ToArray();
var index = 0;
foreach (var parameter in parameters)
{
builder.AppendFormat(
"{0}: {1}",
parameter.Parameter.Name,
rendererMap.FindAndRender(parameter.Argument));
if (++index < parameters.Length)
{
builder.Append(", ");
}
}
builder.Append(")");
writer.Write(builder.ToString());
}
}
希望这能给你一些关于如何解决这个问题的想法。