Razmodius
Razmodius

Reputation: 301

Unable to access StackTrace in custom C# Exception

I recently tried setting up a logging system for my Web API. I decided that I wanted to create custom Exception classes that could be thrown and then logged by my GlobalExceptionHandler. However, I am having some issues accessing the StackTrace of my custom Exception objects.

I set up a custom Exception class called LoggableException which I use as a parent class for all of my custom Exceptions that I want to log to my database.

    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;
        }
    }

Here is an example of one of my custom 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.")
        { }
    }

However, it seems that whenever I throw one of these LoggableExceptions, a <System.NullReferenceException: Object reference not set to an instance of an object. is also thrown at Log.StackTrace = StackTrace.Substring(0, 1000);. I'm doing this so that I don't store huge StackTrace strings in my database, but StackTrace is always null. I assumed that if I called the base constructor that it would automatically instantiate StackTrace, but I guess I was wrong. Can anyone explain to me what I am doing wrong here? I am still relatively new to C#.

Upvotes: 0

Views: 353

Answers (2)

Yvanddivans
Yvanddivans

Reputation: 31

This is quite old, but there was no actual solution posted yet. I also wanted to automatically create a system log on each new custom exception (even if it's not "best practice"), this is what worked for me.

Although the Stacktrace ain't yet initialised in your constructor, you can create a new instance of 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;
    }
}

This should do what you want.

The only problem is that your LoggableException constructor will also jerk its way in there (Frame zero), but you can always loop all frames to create your Stactrace String, formating the data as you want and skipping frame zero.

Upvotes: 0

Mauricio Ortega
Mauricio Ortega

Reputation: 335

The NullReference exception is thrown because you are accesing the StackTrace property of the instance you are building inside the constructor itself.

You should use

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

or

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

instead to be sure that this.StackTrace is already instantiated

You should take care from the ArgumentOutOfRangeException that can be thrown when StackTrace length is lesser than 1000 characters

Upvotes: 1

Related Questions