I have a method for manually causing the application to crash for testing purposes. It works as supposed to and the custom error page appears just as I want it to. However, I noticed that while the outer exception (i.e. the exception) provides info on location (i.e. Api.Controllers.UtilController.Poof() in UtilController.cs, line 58 on the last line below), the inner one doesn't (stating but Unknown location, while I was led to believe otherwise). The full content of the error message rendered is as follows.
Exception: There are no details but the error code.
GUID: 77ad89f7-7220-43b6-8fe9-d473e18ff07b
See outer text for general info.
Unknown locationException: This is a crash test invoked @ 07:21:15.
See inner exception for details.
Api.Controllers.UtilController.Poof() in UtilController.cs, line 58
I understand that since the inner exception is merely instantiated, not actually thrown, there's no location of its origin. I'd like to alter the location text provided from the inner exception to something custom.
Investigating the contents of the object, I found only two properties seeming to be the likely for that purpose - Source and TargetSite. However, setting those, didn't bring any change in the values (remaining null, despite not being read-only), so I conclude they get overwritten somehow. I also found no field with the string value corresponding to the location anywhere, even after the throw having been invoked.
public ActionResult Poof()
{
string occasion = DateTime.Now.ToString("HH:mm:ss");
string message = "This is a crash test invoked @ " + occasion + "."
+ "\nSee inner exception for details.";
string details = "There are no details but the error code."
+ "\nGUID: " + Guid.NewGuid()
+ "\nSee outer text for general info.";
Exception innerException = new Exception(details);
innerException.Source = "Location as referred below.";
// innerException.TargetSite = new DynamicMethod(...);
Exception exception = new Exception(message, innerException);
throw exception;
}
I've read the docs of the exception page in WebAPI and the general exception object (including the properties such as Source and TargetSite for the InnerException). I feel that I understand the contents but the reality proves otherwise. Effort in research invested, I need help clarifying the three items above.
The main issue here is the fact that only one of the exceptions is actually thrown
.
The outer exception has a location because it was thrown
.
The inner exception was created and added to the outer exception but the inner exception was not a thrown and caught exception. Thus will have no location information populated by the runtime.
For example, the following test example
[TestClass]
public class MyTestClass {
[TestMethod]
public void MyTestMethod() {
string occasion = DateTime.Now.ToString("HH:mm:ss");
try {
string details = "There are no details but the error code." + "\nGUID: " + Guid.NewGuid() + "\nSee outer text for general info.";
Exception innerException = new Exception(details);
innerException.Source = "Location as referred below.";
throw innerException;
} catch (Exception e) {
string message = "This is a crash test invoked @ " + occasion + "." + "\nSee inner exception for details.";
Exception exception = new Exception(message, e);
throw exception;
}
}
}
Shows the following message when the test is run
Result StackTrace:
at UnitTestProject.tests.MyTestClass.MyTestMethod() in C:\...\UnitTest1.cs:line 33
--- End of inner exception stack trace ---
at UnitTestProject.tests.MyTestClass.MyTestMethod() in C:\...\UnitTest1.cs:line 37
Result Message:
Test method UnitTestProject.tests.MyTestClass.MyTestMethod threw exception:
System.Exception: This is a crash test invoked @ 20:34:49.
See inner exception for details. ---> System.Exception: There are no details but the error code.
GUID: 2a9bd143-6647-4774-9ea4-615d7f1d2f9f
See outer text for general info.
If the test is rewritten as follows
[TestClass]
public class MyTestClass {
[TestMethod]
public void MyTestMethod() {
try {
Core();
}catch(Exception ex) {
var temp = ex; //<-- PUT BREAKPOINT HERE
}
}
private static void Core() {
string occasion = DateTime.Now.ToString("HH:mm:ss");
try {
string details = "There are no details but the error code." + "\nGUID: " + Guid.NewGuid() + "\nSee outer text for general info.";
Exception innerException = new Exception(details);
innerException.Source = "Location as referred below.";
throw innerException;
} catch (Exception e) {
string message = "This is a crash test invoked @ " + occasion + "." + "\nSee inner exception for details.";
Exception exception = new Exception(message, e);
throw exception;
}
}
}
And debugged, The caught exception and inner exception will contain all the relevant content. Including the custom source in the inner exception.
Where are those location info strings located in the exception object?
Usually in the StackTrace
When/how do they get assigned?
When an exception is thrown they are populated by the runtime.
The common language runtime (CLR) updates the stack trace whenever an exception is thrown in application code (by using the throw keyword). If the exception was rethrown in a method that is different than the method where it was originally thrown, the stack trace contains both the location in the method where the exception was originally thrown, and the location in the method where the exception was rethrown. If the exception is thrown, and later rethrown, in the same method, the stack trace only contains the location where the exception was rethrown and does not include the location where the exception was originally thrown.
Reference Exception.StackTrace Property
Can I alter them and if so how?
Notes to Inheritors
The StackTrace property is overridden in classes that require control over the stack trace content or format.
By default, the stack trace is captured immediately before an exception object is thrown. Use StackTrace to get stack trace information when no exception is being thrown.
You can consider creating a custom derived exception that inspects the base stack trace to make what ever desired alteration before returning the stack trace.
Custom Exception
public override StackTrace {
get {
string original = base.StackTrace;
string result = //...altered message
return result;
}
}
Reference Implementing custom exceptions