I have been writing .NET applications and have been impressed with the error handling included in the framework.
When catching an error that has been throw by the processes or somewhere in the code I like to include the message (ex.Message, which is usually pretty general) but also the stacktrace (ex.stacktrace) which helps to trace the problem back to a specific spot.
For a simple example let's say for instance that we are recording numbers to a log in a method:
public void ExampleMethod(int number){
try{
int num = number
...open connection to file
...write number to file
}
catch(Exception ex){
.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally{
...close file connection
}
}
Is there any way to see the method called (in this case ExampleMethod) with the specific number that was passed that potentially crashed the method call? I believe you could log this perhaps in the catch block but I am interested essentially in catching the method call and parameters that caused the system to throw the exception.
Any ideas?
I suggest stuffing the parameter values into the exception's Data dictionary, e.g.
public void ExampleMethod(int number) {
try {
int num = number
...open connection to file
...write number to file
}
catch(Exception ex) {
ex.Data["number"] = number;
//.... deal with exception (ex.message,ex.stacktrace etc...)
}
finally {
//...close file connection
}
Another advantage of this method is that you can stuff the parameters in the catch block, then re-throw the exception and log it somewhere else without losing the stack trace, e.g.
catch(Exception ex) {
ex.Data["number"] = number;
throw;
}
If you want to know the value of the parameters in your method, then there is only one way, IMO, to do it - you need to repackage the exception with data.
For example:
int param1 = 10;
string param2 = "Hello World";
try
{
SomeMethod(param1, param2)
}
catch(SomeExpectedException e)
{
throw new MyParameterSensitiveException(e, param1, param2);
}
You basically repackage the original exception as the inner exception of another exception, and additionally supply the parameters you used to call the method. Then you could inspect that in some way to figure out what went wrong.
The accepted answer and many of the solutions described will work fine but what you're doing is littering your source with a slightly different blob of code depending on what parameters are in your method signature.
When it comes time to add a new parameter you need to remember to update your handler to add that new parameter. Or if you remove a parameter then you need to remember to remove the parameter from your exception handler.
What if you have a two or more try..catch blocks? Then you now have two blocks of code to keep up to date. Definitely not refactor friendly.
Another approach is to remove the logging code use a technique called Aspect Oriented Programming.
One such tool to facilitate this is a product called PostSharp.
With PostSharp you can write a logger than is invoked whenever an exception is thrown without the need for messy method and parameter specific code. For example (using version 1.5 of PostSharp):
LoggerAttribute.cs -
[Serializable]
public class LoggerAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
Console.WriteLine(eventArgs.Method.DeclaringType.Name);
Console.WriteLine(eventArgs.Method.Name);
Console.WriteLine(eventArgs.Exception.StackTrace);
ParameterInfo[] parameterInfos = eventArgs.Method.GetParameters();
object[] paramValues = eventArgs.GetReadOnlyArgumentArray();
for (int i = 0; i < parameterInfos.Length; i++)
{
Console.WriteLine(parameterInfos[i].Name + "=" + paramValues[i]);
}
eventArgs.FlowBehavior = FlowBehavior.Default;
}
}
You then decorate your classes with the LoggerAttribute:
[Logger]
public class MyClass
{
public void MyMethod(int x, string name)
{
// Something that throws an exception
}
}
Anything that throws an exception in MyMethod will cause the OnException method to be executed.
There are two versions of PostSharp. Version 1.5 is free and open sourced under the GPL and is targeted at .NET 2.0. PostSharp 2.0 is not entirely free but its community edition will support the basic functionality described above.
In order to do this:
public void MyProblematicMethod(int id, string name)
{
try
{
object o = null;
int hash = o.GetHashCode(); // throws NullReferenceException
}
catch (Exception ex)
{
string errorMessage = SummarizeMethodCall(MethodBase.GetCurrentMethod(), id, name);
// TODO: do something with errorMessage
}
}
...and get this:
"MyProblematicMethod invoked: id = 1, name = Charlie"
...you could do something like this:
public static string SummarizeMethodCall(MethodBase method, params object[] values)
{
var output = new StringBuilder(method.Name + " invoked: ");
ParameterInfo[] parameters = method.GetParameters();
for (int i = 0; i < parameters.Length; i++)
{
output.AppendFormat("{0} = {1}",
parameters[i].Name,
i >= values.Length ? "<empty>" : values[i]
);
if (i < parameters.Length - 1)
output.Append(", ");
}
return output.ToString();
}
You could make a class that inherits Exception and add some arguments to it so you could pass the number to it.
You can get the method name and the parameters like this,
try
{
int a = 0;
int i = 1 / a;
}
catch (Exception exception)
{
StackTrace s = new StackTrace(exception);
StackFrame stackFrame = s.GetFrame(s.FrameCount - 1);
if (stackFrame != null)
{
StringBuilder stackBuilder = new StringBuilder();
MethodBase method = stackFrame.GetMethod();
stackBuilder.AppendFormat("Method Name = {0}{1}Parameters:{1}", method.Name, Environment.NewLine);
foreach (ParameterInfo parameter in method.GetParameters())
{
stackBuilder.AppendFormat("{0} {1}", parameter.ParameterType.FullName, parameter.Name);
stackBuilder.AppendLine();
}
// or use this to get the value
//stackBuilder.AppendLine("param1 = " + param1);
//stackBuilder.AppendLine("param2 = " + param2);
}
}
I am not sure whether you can get the parameter values directly off the stack like a debugger.
The Automatic Exception Handling from Crypto Obfuscator can do what you need.
The exception reports include all pertinent information including full stack trace info along with the values of all method arguments and local variables, plus the system information, the time of the exception, the build number, and optional developer defined custom data like log files, screenshots, etc.
DISCLAIMER: I work for LogicNP Software, the developer of Crypto Obfuscator.
Related
I am trying to find a solution that allows me to write one method in my forms project that can variably call multiple different methods from my class library project.
The reason for this being that I want to implement retry logic around these methods and prevent myself from repeating it for each different variety of method. The only consistent thing about the class library methods are that they all return Task<bool> so its easy to await on and perform logic with.
So far I have the following:
public async Task Runner(string methodName, params object[] parameters)
{
ThreadTimer.Start();
var tries = 0;
var ok = false;
while (tries <= 180)
{
try
{
var parameterTypes = (from p in parameters select p.GetType()).ToArray();
var mi = typeof(string).GetMethod(methodName, parameterTypes); //Currently returns null
var result = (Task<bool>)mi.Invoke(null, parameters);
ok = await result;
if (ok) break;
}
catch (Exception ex)
{
if (ex.InnerException == null)
{
ExceptionLabel2.Text = ex.Message;
}
else
{
ExceptionLabel1.Text = ex.Message;
ExceptionLabel2.Text = ex.InnerException.Message;
}
}
finally
{
tries++;
}
}
if (ok)
{
ThreadTimer.Dispose();
}
else
{
CShellControls.ExitWindowsEx(0, 0); //Logoff
}
}
The idea behind this is to declare a method name in a string and pass an array of parameters with it. I then used .GetMethod() to try and fetch the desired method info but unfortunately this returns null.
I have tried a few different methods but I'm open to suggestions and critique. As far as optimizing code goes I haven't really thought much into it, I just want to try and get this working first before approaching a more efficient method.
Thank you in advance!
My code looks as below.
try
{
_productRepo.GetAllProductCategories();
}
catch (Exception ex)
{
//Do Something
}
I need a way to show the method name, suppose in the above case if any exception is thrown in the GetAllProductCategories() method, I need to get this method name i.e. "GetAllProductCategories()" as my result. Can any one suggest me how to do this?
There's a TargetSite property on System.Exception that should come in handy.
Gets the method that throws the
current exception.
In your case, you probably want something like:
catch (Exception ex)
{
MethodBase site = ex.TargetSite;
string methodName = site == null ? null : site.Name;
...
}
It's worth pointing out some of the issues listed:
If the method that throws this
exception is not available and the
stack trace is not a null reference
(Nothing in Visual Basic), TargetSite
obtains the method from the stack
trace. If the stack trace is a null
reference, TargetSite also returns a
null reference.
Note: The TargetSite property may not
accurately report the name of the
method in which an exception was
thrown if the exception handler
handles an exception across
application domain boundaries.
You could use the StackTrace property as #leppie suggests too, but do note that this is a string representation of the frames on the stack; so you'll have to manipulate if you only want the name of the method that threw the execption.
It's in the StackFrame...
private string GetExecutingMethodName()
{
string result = "Unknown";
StackTrace trace = new StackTrace(false);
Type type = this.GetType();
for (int index = 0; index < trace.FrameCount; ++index)
{
StackFrame frame = trace.GetFrame(index);
MethodBase method = frame.GetMethod();
if (method.DeclaringType != type && !type.IsAssignableFrom(method.DeclaringType))
{
result = string.Concat(method.DeclaringType.FullName, ".", method.Name);
break;
}
}
return result;
}
This method was written for an Logging handler class and the use of GetType() simply eliminates the methods within the Logging handler class from being returned as the last executing method. Since the Logging handler class was written for more than just logging exceptions, a new StackTrace object was required. Obviously, for finding "the method that threw the exception" GetType() might not be necessary.
If you just want the top of the stack, take the first frame, call GetMethod() and return that, or simply use TargetSite. GetType() could then be removed. Also note, that the Exception would need to be passed in to create the StackTrace object. For example:
class Program
{
static void Main(string[] args)
{
try
{
Test();
}
catch (Exception ex)
{
// does not work properly - writes "Main"
Console.WriteLine(MethodBase.GetCurrentMethod());
// properly writes "TestConsole.Program.Test"
Console.WriteLine(GetExecutingMethodName(ex));
// properly writes "Test"
Console.WriteLine(ex.TargetSite.Name);
}
Console.ReadKey();
}
static void Test()
{
throw new Exception("test");
}
private static string GetExecutingMethodName(Exception exception)
{
var trace = new StackTrace(exception);
var frame = trace.GetFrame(0);
var method = frame.GetMethod();
return string.Concat(method.DeclaringType.FullName, ".", method.Name);
}
}
Basically, if TargetSite() does what you want, then go no further. But, often times in Logging handlers, an exception object is not available (i.e. tracing and auditing) so a new StackTrace() object is necessary for retrieving the last executed method, the one BEFORE the Logging method.
Look at the stacktrace.
It's a property on the exception.
Our C# codebase have several methods that create error messages that include the method's name. Can I get the compiler to statically insert the method name for me? I know I could do something with reflection, but I'd rather not.
Amongst other things, I'm seeing quite a few copy-paste errors where the exception handling from one method is copied to another, without the method name changing.
public void Method1()
{
try
{
DoStuff();
}
catch (Exception e)
{
HandleError("Method1", details);
}
}
Rather than include the string "Method1" (and "Method2" up to "Methodn") is there a way of telling the compiler to insert the current method name there?
In NET 4.5 you can use the CallerMemberName attribute. Your HandleError method would then look like so:
void HandleError(YourDetailsClass details,
[CallerMemberName] callingMethod = null)
and you'd simply use
HandleError(details);
you can use MethodBase.GetCurrentMethod which returns MethodInfo
using System.Reflection;
and then
catch (Exception e)
{
HandleError(MethodBase.GetCurrentMethod().Name, details);
}
Yes, you can try with this:
System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
string methodName = st.GetFrame(0).GetMethod().Name;
And you will have the name of your running method.
One way is to use the StackTrace and StackFrame classes in System.Diagnostics to retrieve the method name:
private void HandleError(Exception ex) {
var st = new StackTrace ();
var sf = st.GetFrame (1); // get the previous method that called this
// (not this method)
var previousMethod = sf.GetMethod ();
var errorMessage = string.Format("Error in method {0} with Exception {1}",
previousMethod.Name,
ex.Message);
}
Example:
void MyMethod() {
HandleError(new Exception("Error here"));
}
errorMessage would contain: Error in method MyMethod with Exception Error here.
I have the following code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
try
{
this.CheckValue(true); // call method
}
catch(Exception ex)
{
// how to get here name of last called method
}
}
public int CheckValue(bool sender)
{
var qwe = int.Parse("qwe"); // invoke an exception
return 0;
}
}
I need to get in "catch block" name of last called method (in this case "CheckValue"), but it return that called method is "StringToNumber".
I try to get it using StackTrace:
stackTrace.GetFrame(1).GetMethod().Name; -> "Main"
MethodBase.GetCurrentMethod(); -> "Void .ctor()"
ex.TargetSite.Name; -> "StringToNumber"
It's possible to do this?
Short Answer:
Yes, You can!!!
I'd just play around with Extension Methods and the trick here, is to get the last frame of the desired class, otherwise it would get methods of mscorlib assembly. So here it go:
public static string GetLastCalledMethod<T>(this Exception ex)
{
var stackTrace = new System.Diagnostics.StackTrace(ex);
var lastFrame = stackTrace.GetFrames().FirstOrDefault(frame => frame.GetMethod().DeclaringType.FullName == typeof(T).FullName);
string methodName = string.Empty;
if (lastFrame != null)
methodName = lastFrame.GetMethod().Name;
return methodName;
}
Short Answer:
You can't.
Long Answer:
If you really need to do that, you will need to perform logging code in all the methods you want to track.
You can create a global variable (ugh) to store a MethodInfo with the last called method, and inside every method, set it to MethodBase.GetCurrentMethod(). Then whenever you want, you can examine that variable to see which method set it last.
In your case, you probably are trying to determine which method the exception was thrown in. You are looking at TargetSite, which returns the lowest method in the hierarchy, whereas you seem to want the one immediately below the current method. If simply examining Exception.StackTrace doesn't provide enough information, you might be able to parse out information from StackTrace and use reflection to get a MethodInfo. Usually, the StackTrace is good enough.
You may also be able to throw a new exception in the top-level method, so you can get the TargetSite from the new one.
Summary:
If Exception.StackTrace doesn't provide enough information, then you will either have to:
Perform logging code in each method you want to check for.
Parse out what information you can get from the Exception.
Change the exception throwing scheme to throw a new exception with InnerException set to the original exception.
I don't know why you want to do this.. because this is expected behaviour. The site of the exception is what you're being shown.. within the int.Parse() calls.
That being said.. if you really want to do this, you need to wrap a try.. catch in CheckValue, and re-throw the exception from there, but in a way that breaks the call stack.. like so:
public int CheckValue(bool sender) {
try {
var qwe = int.Parse("qwe"); // invoke an exception
return 0;
}
catch (Exception ex) {
throw ex; // this breaks the call stack and re-throws the exception from here..
}
}
Then, ex.TargetSite.Name == "CheckValue". I'm still not sure why you'd want to do this.. as a stack trace will actually show you where it all unwinds from after failure.
I want to get line number of code which cause error. For example;
static void Main(string[] args)
{
using (SqlConnection conn = new SqlConnection(bagcum))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "DONTINSERT into GIVEMEERROR(CamNo,Statu) values (" + 23 + "," + 0 + ")";
conn.Open();
int n = cmd.ExecuteNonQuery();
}
}
so As we know that code doesn't work, it will throw exception Line number of code which is:
int n = cmd.ExecuteNonQuery();
So how can get that line number of using try-catch? I tried using a StackTrace class but it gives line number as 0:
static void Main(string[] args)
{
try
{
using (SqlConnection conn = new SqlConnection(bagcum))
{
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "DONTINSERT into GIVEMEERROR(CamNo,Statu) values (" + 23 + "," + 0 + ")";
conn.Open();
int n = cmd.ExecuteNonQuery();
}
}
catch (Exception ex)
{
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
}
}
OUTPUT:
Line:0
Update:
Usually error line of code is 22 so I have to get that number.
Thanks
Try this simple hack instead:
First Add this (extension) class to your namespace(most be toplevel class):
public static class ExceptionHelper
{
public static int LineNumber(this Exception e)
{
int linenum = 0;
try
{
//linenum = Convert.ToInt32(e.StackTrace.Substring(e.StackTrace.LastIndexOf(":line") + 5));
//For Localized Visual Studio ... In other languages stack trace doesn't end with ":Line 12"
linenum = Convert.ToInt32(e.StackTrace.Substring(e.StackTrace.LastIndexOf(' ')));
}
catch
{
//Stack trace is not available!
}
return linenum;
}
}
And its done!Use LineNumber method whenever you need it:
try
{
//Do your code here
}
catch (Exception e)
{
int linenum = e.LineNumber();
}
try this
To get the line numbers in the StackTrace, you need to have the correct debug information (PDB files) alongside your dlls/exes. To generate the the debug information, set the option in Project Properties -> Build -> Advanced -> Debug Info:
Setting it to full should suffice (see the MSDN docs for what the other options do). Debug info (ie. PDB files) are generated for Debug build configurations by default, but can also be generated for Release build configurations.
Generating PDBs for release builds enables you to ship you code without the PDBs, but to drop the PDBs next to the dlls if you need line numbers (or even to attach a remote debugger). One thing to note is that in a release build, the line numbers may not be entirely correct due to optimisations made by the compiler or the JIT compiler (this is especially so if the line numbers show as 0).
The problem is that you're trying to get the line number of the first frame of the exception:
System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);
Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
However, the exception does not originate in the line you write ExecuteNonQuery, but somewhere within that function, possibly multiple stack frames (i.e. nested function calls) deeper. So the first frame (which you explicitly retrieve using GetFrame(0)) is somewhere inside Microsoft's code (most likely System.Data.dll) for which you don't have any debugging symbols.
Write out the complete exception stacktrace in your function to see what I mean:
try
{
// your code ...
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Short of parsing the stacktrace (i.e. ex.StackTrace) there is no reliable why to get the linenumber of the "ExecuteNonQuery()" invocation. I would especially not try to count the stackframes up the stack where your call to ExecuteNonQuery() happens.
I wonder however, what you need the sole linenumber for, why not just log/print/whatever the complete stacktrace instead. At least for diagnostics reasons that is much more useful anyway.
You might get 0 in result if you don't initialize StackTrace to include fileinfo.
Try this
try
{
//code
}
catch (Exception e)
{
var lineNumber = new System.Diagnostics.StackTrace(e, true).GetFrame(0).GetFileLineNumber();
}
This worked for me.
You can use the System.Diagnostics.StackTrace class as below:
public void MethodName()
{
try
{
throw new Exception();
}
catch (Exception ex)
{
// Get stack trace for the exception with source file information
var trace = new StackTrace(ex, true);
// Get the top stack frame
var frame = trace.GetFrame(0);
// Get the line number from the stack frame
var line = frame.GetFileLineNumber();
}
}
Here's a rather easy way to get a bunch of info from the Exception object: Just add code like this to any potentially exception-throwing methods:
catch (Exception ex)
{
String exDetail = String.Format(ExceptionFormatString, ex.Message, Environment.NewLine, ex.Source, ex.StackTrace);
MessageBox.Show(exDetail);
}
The information you get will often be more specific, especially as regards line numbers of where problems are occurring, than you would otherwise see.
You may have noted that the String.Format() uses a constant, namely "ExceptionFormatString". This is a good practice, so that if you want to change it, after adding the above code to 40-eleven methods, you can just change it one place. Anyway, here it is:
public static readonly String ExceptionFormatString = "Exception message: {0}{1}Exception Source: {2}{1}Exception StackTrace: {3}{1}";
Happy Debugging!
To get line numbers, you need your application to be in Debug mode or include the debug symbols in the same folder (the .pdb file) for line numbers to appear. You code as posted should then work.
the following code exception log handler method is works fine :
in catch :
catch (Exception ex)
{
CommonTools.vAddToLog(ex, EmpID, ErrorCodes.UnDefined);
Response.Redirect("~/ErrorPage.aspx");
}
in AddToLog method :
string _exMsgErr = string.Empty;
var frame = oStackTrace.FrameCount > 1 ? oStackTrace.GetFrame(1) : oStackTrace.GetFrame(0);
if (oException.GetType() == typeof(JOVALException))
{
JOVALException _JOVALEx = (JOVALException)oException;
_exMsgErr = _JOVALEx.Message;
}
else
{
_exMsgErr = oException.Message;
}
ErrorLog oError = new ErrorLog(frame.GetMethod().Name, (string)frame.GetFileName(), (int)frame.GetFileLineNumber(), sCustomErrorMessage == string.Empty ? _exMsgErr : sCustomErrorMessage, sUserID, oErrCode);
//Cont. your code of log file
Finally the xml log file looks like this :
<ErrorLog>
<MethodName>FillRolesDDLs</MethodName>
<FileName>
F:\Projects\ERP\ERP\Pages\SystemSettings\Roles.aspx.cs
</FileName>
<LineNumber>61</LineNumber>
<ErrorMesssage>
The given DataRow is not in the current DataRowCollection.
</ErrorMesssage>
<UserID>1</UserID>
<ErrCode>UnDefined</ErrCode>
<Time>15/03/2015 16:23:21.976</Time>
</ErrorLog>
In .NET 4.5 you can use the ExceptionDispatchInfo to rethrow your exceptions instead of the classic throw;(make sure the PDB files are there or no line numbers will be displayed):
static void A()
{
try
{
throw new Exception("A");
}
catch (Exception e)
{
ExceptionDispatchInfo.Capture(e).Throw();
}
}
Source: blogpost.
PDB files don't decrease performance on Windows.
Copy the entire stack trace in to a string or stringbuilder by using try/catch that can throw, see the below example
try
{
//Do some programming
}
catch(Exception ex)
{
//Catch the exception and assign the stack trace
StackTrace = ex;
}
The output will be
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at Program.Run() in C:\Console Application1\Program.cs:line 37
at Program.Main(String[] args) in C:\Console Application1\Program.cs:line 45
The first line shows the type of the exception and the message. The second line shows the file, function and line number where the exception was thrown