wcf 上的详细异常日志记录

本文关键字:异常 日志 记录 wcf | 更新日期: 2023-09-27 18:00:48

>最近我正在研究WCF服务的异常日志记录模块。不幸的是,该服务尚未随单元测试一起引入,因此发生了许多意外的异常。到目前为止,我已经通过实现IErrorHandler接口并将其绑定到具有IServiceBehaviour的服务接口,完成了使用拦截器aproach获取异常。实际上,我非常喜欢此功能。但它让我进入了下一步的愿望,即获得异常的细节。比如异常发生在哪一行?

我可以通过两种方式满足这种愿望:

  1. 通过有一个变量来跟踪我成功通过的行,并将其包含在抛出的异常中。
  2. 通过单独捕获所有行的异常。

但这两种方法对我来说似乎都很糟糕。我想知道是否有已知的设计模式或工具来实现此目标?

wcf 上的详细异常日志记录

在我看来,你可以尝试使用日志记录,比如log4net。然后你可以找出在哪里以及发生了什么。异常对象并不总是包含堆栈信息,因为在优化等过程中发生的"内联"。

包含服务的 PDB 文件,行号将包含在exception.ToString()

我们解决这个问题的方法有两个:

  1. 我们的服务是命令的愚蠢包装器。因此,当输入服务方法时,它会将其工作委托给命令。
  2. 我们将每个命令调用包装在负责记录输入、输出和错误并执行命令的日志记录代理中。

例如:

public FooServiceModel GetFoo(int fooId)
{
   return new ILogged<GetFooCommand>().Target.Execute(fooId);
}

这会将命令的执行委托给ILogged,其中:

  1. 记录命令名称
  2. 记录命令参数
  3. 记录执行结果
  4. 记录任何异常

它还执行一些其他操作,使用自定义消息标头将客户端请求与服务器调用链接起来,以便可以从客户端到服务器并返回完全调试调用。这非常有用,使我们能够在异地诊断甚至复杂的问题。

我们使用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());
    }
}

希望这能给你一些关于如何解决这个问题的想法。