【问题标题】:Unable to access StackTrace in custom C# Exception无法在自定义 C# 异常中访问 StackTrace
【发布时间】:2020-12-26 19:24:01
【问题描述】:

我最近尝试为我的 Web API 设置日志系统。我决定要创建自定义的异常类,这些类可以被我的 GlobalExceptionHandler 抛出然后记录下来。但是,我在访问自定义异常对象的 StackTrace 时遇到了一些问题。

我设置了一个名为 LoggableException 的自定义异常类,我将它用作我想要记录到我的数据库的所有自定义异常的父类。

    public class LoggableException : Exception
    {
        public LogEntity Log { get; set; }
        public string ResponseMessage { get; set; }

        public LoggableException(LogEntity _log, string _responseMessage) : base(_log.ExceptionMessage)
        {
            Log = _log;
            Log.Level = LogLevel.Warn;
            Log.Exception = GetType().Name;
            Log.StackTrace = StackTrace.Substring(0, 1000);

            ResponseMessage = _responseMessage;
        }
    }

这是我的一个自定义 LoggableExceptions 的示例:

    public class InvalidPassword : LoggableException
    {
        public InvalidPassword(string email) : base(new LogEntity()
        {
            ExceptionMessage = $"Invalid password. email: {email}",
            ResponseCode = (int) HttpStatusCode.Unauthorized
        }, $"Unknown email or incorrect password.")
        { }
    }

但是,似乎每当我抛出这些 LoggableExceptions 之一时,<System.NullReferenceException: Object reference not set to an instance of an object. 也会被抛出 Log.StackTrace = StackTrace.Substring(0, 1000);。我这样做是为了不在我的数据库中存储巨大的 StackTrace 字符串,但 StackTrace 始终为空。我假设如果我调用基本构造函数它会自动实例化 StackTrace,但我想我错了。谁能向我解释我在这里做错了什么?我对 C# 还是比较陌生。

【问题讨论】:

  • 仅使用自定义异常类型进行日志记录是有问题的;实际上登录该异常的构造函数是致命的缺陷(正如您所发现的)。堆栈跟踪不可用,因为在构造时,甚至还没有抛出异常——堆栈展开以查找处理程序仅在该点完成。异常处理程序应该执行任何所需的日志记录,而不是异常本身。对于专门的日志记录,只需在调用站点直接使用记录器(如果需要,可以借助 StackTrace 类),不要让异常执行。
  • 好的,这完全有道理。感谢您的帮助!

标签: c# asp.net exception stack-trace


【解决方案1】:

这已经很老了,但是由于还没有发布实际的解决方案,我还想在创建的每个自定义异常上自动创建一个系统日志(即使它不是“最佳实践”)。

虽然 Stacktrace 尚未在您的构造函数中初始化,但您仍然可以create an instance of a new StackTrace

public class LoggableException : Exception
{
    public LogEntity Log { get; set; }
    public string ResponseMessage { get; set; }

    public LoggableException(LogEntity _log, string _responseMessage) : base(_log.ExceptionMessage)
    {
        Log = _log;
        Log.Level = LogLevel.Warn;
        Log.Exception = GetType().Name;
        Log.StackTrace = (new System.Diagnostics.StackTrace()).ToString();
        if (Log.StackTrace.Length > 1000)
            Log.StackTrace = Log.StackTrace.Substring(0, 1000);

        ResponseMessage = _responseMessage;
    }
}

这应该做你想做的。

唯一的问题是您的 LoggableException 构造函数也会在其中猛拉(零帧),但您始终可以循环所有帧创建您的 Stactrace 字符串,根据需要格式化数据。

【讨论】:

    【解决方案2】:

    NullReference 异常被抛出,因为您正在访问您在构造函数内部构建的实例的 StackTrace 属性。

    你应该使用

     Log.StackTrace = StackTrace?.Substring(0, 1000);
    

     if(StackTrace!=null) Log.StackTrace=StackTrace.Substring(0, 1000);
    

    而是确保 this.StackTrace 已经实例化

    您应该注意当 StackTrace 长度小于 1000 个字符时可能引发的 ArgumentOutOfRangeException

    【讨论】:

    • 检查 StackTrace 是否不是 null 对实例化它没有任何作用。这仅仅意味着您将永远不会有堆栈跟踪,而不是收到错误 - 但这也可以通过完全省略这一行来实现。
    猜你喜欢
    • 2021-12-13
    • 1970-01-01
    • 2011-09-22
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 2013-03-26
    • 2013-11-23
    相关资源
    最近更新 更多