【问题标题】:Generic logging of function parameters in exception handling异常处理中函数参数的通用日志记录
【发布时间】:2010-09-13 05:48:16
【问题描述】:

我的很多 C# 代码都遵循这种模式:

void foo(string param1, string param2, string param3)
{
    try
    {
         // do something...
    }
    catch(Exception ex)
    {
        LogError(String.Format("Error in foo(param1={0}, param2={1}, param3={2}), exception={3}", param1, param2, param3, ex.Message));
    }
}

.NET 中是否有一种方法可以获取函数参数的键/值列表,以便我可以调用另一个函数来构造我的错误日志记录字符串? 要么 您有更通用/更好的方法吗?

【问题讨论】:

  • 可能是某种语言中的反射可以提供帮助?
  • 您可以通过反射来实现,但在许多函数调用过程中会变得很昂贵。
  • 我不认为你可以用反射或 StackTrace/StackFrame 做到这一点。我认为您唯一的选择可能是使用 AOP 或后处理框架,例如 PostSharp。

标签: c# .net


【解决方案1】:

完成此操作后,我刚刚为日志记录创建了一个通用字典。

我有这个 LogArgs 类。并登录我在遇到异常时调用的基类。

public class LogArgs
{

    public string MethodName { get; set; }
    public string ClassName { get; set; }
    public Dictionary<string, object> Paramters { get; set; }


    public LogArgs()
    {
        this.Paramters = new Dictionary<string, object>();
    }

}

然后在我做的每一个方法的开始

LogArgs args = new LogArgs { ClassName = "ClassName", MethodName = "MethodName" };
args.Paramters.Add("Param1", param1);
args.Paramters.Add("Param2", param2);
args.Paramters.Add("Param3", param3);

base.Logger.MethodStartLog(args);

当我遇到错误时,我会以这种方式记录。

base.Logger.LogError(args, ex);

【讨论】:

  • 有趣的想法,但是当你听到自己说“在每个方法的开始我都做 X”时,考虑使用 AOP 框架(如 PostSharp,其他用户建议的)。
【解决方案2】:

不,没有办法做到这一点。

通常的做法是不捕获异常,除非你能处理它们。

即您通常只会捕获异常并将它们记录在顶级异常处理程序中。然后您将获得堆栈跟踪,但当然不会获得堆栈中所有方法调用的所有参数的详细信息。

显然,在调试时,您需要尽可能多的细节。实现此目的的其他方法是:

  • 大量使用 Debug.Assert 语句来测试您所做的假设。

  • 使用可以选择性激活的日志记录来检测您的应用程序。我使用 Log4Net,但也有其他选择,包括使用 System.Diagnostics.Trace 类。

在任何情况下,如果您确实捕获异常只是为了记录它们(我会在 n 层应用程序的层边界处执行此操作,以便将异常记录在服务器上),那么您应该始终重新抛出它们:

try
{
    ...
}
catch(Exception ex)
{
    log(ex);
    throw;
}

【讨论】:

  • 这会展开堆栈,这意味着差异调用堆栈将被记录而不是重新抛出。为了避免这种情况,现在可以利用异常过滤器:try { // ... } catch (Exception ex) when (log(ex)) { // never entered } where function log always return false
  • 只要你不throw ex; 而只是throw; 堆栈和异常仍然存在。
【解决方案3】:

您可以使用类似的方式构造消息,但在 LogError 方法中添加 params 关键字来处理参数。例如:

    public void LogError(string message, params object[] parameters)
    {
        if (parameters.Length > 0)
            LogError(string.Format(message, parameters));
        else
            LogError(message);
    }

【讨论】:

    【解决方案4】:

    您可以使用反射和必须以正确的顺序将参数传递给 LogError 的约定:

    private static void MyMethod(string s, int x, int y)
    {
        try
        {
            throw new NotImplementedException();
        }
        catch (Exception ex)
        {
            LogError(MethodBase.GetCurrentMethod(), ex, s, x, y);
        }
    }
    
    private static void LogError(MethodBase method, Exception ex, params object[] values)
    {
        ParameterInfo[] parms = method.GetParameters();
        object[] namevalues = new object[2 * parms.Length];
    
        string msg = "Error in " + method.Name + "(";
        for (int i = 0, j = 0; i < parms.Length; i++, j += 2)
        {
            msg += "{" + j + "}={" + (j + 1) + "}, ";
            namevalues[j] = parms[i].Name;
            if (i < values.Length) namevalues[j + 1] = values[i];
        }
        msg += "exception=" + ex.Message + ")";
        Console.WriteLine(string.Format(msg, namevalues));
    }
    

    【讨论】:

    • 不错的一个。对象如何处理?喜欢人或用户。
    • Jeeva Jsb 对于对象你可以尝试使用一些 json 序列化,例如: string output = JsonConvert.SerializeObject(user);
    【解决方案5】:

    您可以在 PostSharp 中使用面向方面的编程(查看http://www.postsharp.orghttp://www.codeproject.com/KB/cs/ps-custom-attributes-1.aspx 上的教程)。基本上你可以这样做:

    public class LogExceptionAttribute : OnExceptionAspect
    {
     public override void OnException(MethodExecutionEventArgs eventArgs)
     {
      log.error("Exception occurred in method {0}", eventArgs); 
     }
    }
    
    [LoggingOnExceptionAspect]
    public foo(int number, string word, Person customer)
    {
       // ... something here throws an exception
    }
    

    也许不是你想要的,但我相信它可以适应你的需要。

    【讨论】:

    【解决方案6】:

    有少量参数或大量参数的场景...

    1. 几个参数,事不宜迟,最好将它们写为日志记录/异常消息的一部分。

    2. 在大参数中,多层应用程序将使用实体(如客户、客户订单...)在层之间传输数据。这些实体应实施 覆盖 Object 类的 ToString() 方法,然后,

    Logmessage(" method started " + paramObj.ToString()) 将给出对象中的数据列表.. 有什么意见吗? :)

    谢谢

    【讨论】:

      【解决方案7】:

      这是一个有点过时的帖子,但以防万一有人像我一样遇到这个问题 - 我通过使用 PostSharp 解决了这个问题。

      但它实际上并不是免费的。 Express 许可证(可通过 VS 中的 NuGet 下载)允许您使用 [Log] 属性装饰您的方法,然后选择您已配置的日志记录机制,例如 log4net nLog 等。现在您将开始在您的日志提供参数详细信息。

      使用 express 许可证,我最多只能在我的项目中装饰 50 种方法。如果它符合您的需求,您就可以开始了!

      【讨论】:

        【解决方案8】:

        聚会迟到了,但大约一年前我做了一些类似的事情: Github Repo

        这种设置的想法很像您的追求,但由于能够在全球范围内连接它,那里的代码比我想要的要多,但它可以工作,一旦插入,就可以满足您的追求。

        如果您快速浏览一下 ProxyLogger.cs,将其视为一个包装器,它将封装给定的任何方法并在处理此处设置的错误日志记录时执行它。然后可以通过依赖注入为您希望记录的任何内容进行设置,例如:

        public void ConfigureServices(HostBuilderContext hostBuilder, IServiceCollection services)
            {
                services.AddOptions();
                services.AddSingleton<IHostedService, HostedService>();
        
                services.AddSingleton<IMyClass, MyClass>();
        
                // Logging added for both the hosted service and the example class
                services.Decorate<IMyClass, ProxyLogger<IMyClass>>();
                services.Decorate<IHostedService, ProxyLogger<IHostedService>>();
            }
        

        您可以正常注册服务,但最重要的是,您可以使用代理记录器来装饰它们以处理执行并使用完整参数记录之前、之后、失败等的详细信息。 我沉迷于这个有一段时间了,这是我能得到的最好的,但它真的很好用。

        【讨论】:

          猜你喜欢
          • 2010-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-20
          • 2020-10-01
          • 1970-01-01
          • 2011-07-08
          • 2010-09-09
          相关资源
          最近更新 更多