wcf上的详细异常日志记录

时间:2014-04-30 07:28:04

标签: c# wcf design-patterns exception-handling

最近我正在研究WCF服务的异常日志记录模块。遗憾的是,该服务还没有引入单元测试,因此出现了许多意外的异常。到目前为止,我已经完成了通过实现IErrorHandler接口并使用IServiceBehaviour将其绑定到服务接口来获取拦截器aproach的异常。我非常喜欢这个功能。但它让我进入下一步,希望获得异常细节。就像发生异常的那条线一样?

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

  1. 通过一个变量来跟踪我已成功通过的行,并将其包含在抛出的异常中。
  2. 通过单独捕获所有行的异常。
  3. 但这两种方法对我来说都很糟糕。我想知道是否有一个已知的设计模式或实现这一目标的工具?

3 个答案:

答案 0 :(得分:0)

在我看来,您可能会尝试使用日志记录,例如log4net。然后你可以找出发生的地点和发生的事情。异常对象并不总是包含堆栈信息,因为在优化等过程中会出现“内联”。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

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

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

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

    1. 记录命令名称
    2. 记录命令参数
    3. 记录执行结果
    4. 记录任何例外
    5. 它还使用自定义消息头将客户端请求与服务器调用链接起来,以便可以从客户端到服务器完全调试并返回调用。这非常有用,可以让我们在现场诊断出复杂的问题。

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

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