16

I need two methods, one for getting the Class from where the exception was called, and another one which gets the line number where an exception was called.

So far I have this code, which gets me the Class Name and Line Number together (example: DatabaseHandler.cs:line 70):

    private string GetClassAndLine()
    {
        string tempName = e.GetBaseException().ToString();
        int tempPosition = 0;
        int length = tempName.Length;
        for (int i = 0; i < length; i++)
        {
            if (tempName.ElementAt(i).Equals('\\'))
            {
                tempPosition = i + 1;
            }
        }
        return tempName.Substring(tempPosition, length - tempPosition);
    }

So if you have any ideas how I could get them individually, it would be of great help. These are then passed into Oracle to store any exceptions which occur.

Update 2:

I am currently testing this code, as some suggested:

        private string GetClassName()
    {
        System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(e, true); 
        return trace.GetFrame(0).GetMethod().ReflectedType.FullName;
    }

    private int GetLineNumber()
    {
        System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(e, true); 
        return trace.GetFrame(0).GetFileLineNumber();
    }

This is what was returned at a particular Database Exception. No Line Number or Class Name where it was triggered. How can I get that?

    Error was found at Class: Oracle.DataAccess.Client.OracleException.     
    Line Number: 0

What I want is for Example: "Class: Logging.cs, Line: 57"

Thanks, Ryan

1
  • It works for me, but after making some adaptations (I am using Compact Framework 3.9): - Frame.GetFileLineNumber runs but always cause an exception. I removed it. - The level of the Frame considered is dependent of your call stack. In my case I have to consider GetFrame(1) because I have to bypass the call to my log() function Commented Mar 25, 2020 at 13:22

3 Answers 3

23

You can do like this

try
{
    // Some code that can cause an exception.

    throw new Exception("An error has happened");
}
catch (Exception ex)
{
    System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(ex, true);

    Console.WriteLine(trace.GetFrame(0).GetMethod().ReflectedType.FullName);
    Console.WriteLine("Line: " + trace.GetFrame(0).GetFileLineNumber());
    Console.WriteLine("Column: " + trace.GetFrame(0).GetFileColumnNumber());
}
Sign up to request clarification or add additional context in comments.

2 Comments

I thought, You have no source files of Oracle.DataAccess.Client Or that is RELEASE version with /OPTIMIZE Option. So, you cannot get any further information from that.
Do you know? that's an example.
4

System.Environment.StackTrace. Useful any time, not just on exceptions. Might need a bit of string manipulation using that though;

If you prefer something a little more hi-tech, you can use System.Diagnostics.StackFrame; Check the object browser for full details, but it's got methods for finding the filename, line and column number, etc.

4 Comments

I am still not sure how to get the class using the Environment.StackTrace, could you please provide an example for getting the class
Do you mean the specific instance? If so, I think that's impossible. Believe me, I've tried. If you mean the type, then you can use the GetMethod().DeclaringType method on the StackFrame.
@Flnn1179 - it's ok, thanks to the 2nd post I know what you wanted to say, check my Update 2, above. I am currently testing that because I got some errors in other parts of my code.
@Flnn1179 - It did not work exactly as I expected, is there any better way? See my update for output + code
2

I'm using the following class DebugE to trace exceptions and log stuff in the application:
It outputs the Thread# DateTime, ClassName.MethodName, Line & Col numbers and the exception message.

using System;
using System.Diagnostics;
using System.Threading;
using System.Reflection;
using System.Text.RegularExpressions;
namespace YourApp
{
class DebugE
{
    public static void d(Exception e)
    {
        try
        {
            MethodBase site = e.TargetSite;//Get the methodname from the exception.
            string methodName = site == null ? "" : site.Name;//avoid null ref if it's null.
            methodName = ExtractBracketed(methodName);

            StackTrace stkTrace = new System.Diagnostics.StackTrace(e, true);
            for (int i = 0; i < 3; i++)
            {
                //In most cases GetFrame(0) will contain valid information, but not always. That's why a small loop is needed. 
                var frame = stkTrace.GetFrame(i);
                int lineNum = frame.GetFileLineNumber();//get the line and column numbers
                int colNum = frame.GetFileColumnNumber();
                string className = ExtractBracketed(frame.GetMethod().ReflectedType.FullName);
                Trace.WriteLine(ThreadAndDateInfo + "Exception: " + className + "." + methodName + ", Ln " + lineNum + " Col " + colNum + ": " + e.Message);
                if (lineNum + colNum > 0)
                    break; //exit the for loop if you have valid info. If not, try going up one frame...
            }

        }
        catch (Exception ee)
        {
            //Avoid any situation that the Trace is what crashes you application. While trace can log to a file. Console normally not output to the same place.
            Console.WriteLine("Tracing exception in d(Exception e)" + ee.Message);
        }
    }
    public static void d(string str)
    {
        try
        {
            StackFrame frame = new StackFrame(1);
            var method = frame.GetMethod();
            string name = ExtractBracketed(method.Name);//extract the content that is inside <brackets> the rest is irrelevant 

            Trace.WriteLine(ThreadAndDateInfo + method.DeclaringType + "." + name + ": " + str);
        }
        catch (Exception e)
        {
            Console.WriteLine("Tracing exception in d(string str)" + e.Message);
        }
    }

    private static string ExtractBracketed(string str)
    {
        string s;
        if (str.IndexOf('<') > -1) //using the Regex when the string does not contain <brackets> returns an empty string.
            s = Regex.Match(str, @"\<([^>]*)\>").Groups[1].Value;
        else
            s = str; 
        if (s == "")
            return  "'Emtpy'"; //for log visibility we want to know if something it's empty.
        else
            return s;

    }

    public static string ThreadAndDateInfo
    {
        //returns thread number and precise date and time.
        get { return "[" + Thread.CurrentThread.ManagedThreadId + " - " + DateTime.Now.ToString("dd/MM HH:mm:ss.ffffff") + "] "; }
    }
}
}

Call if from anywhere in your application:

catch (Exception e) 
{
    DebugE.d(e);
}

To use it for plain logging, call DebugE.d("some text");
It will return the same but without the line and col numbers.

Note that for production applications I always end up calling a "simpler" call systematically:
void debug(string methodName, exception e)
.
This is because the "nicer" solution above DID NOT ALWAYS RETURN THE CORRECT METHOD information to me. If my guess is correct, this would be the case if your exception occurs in a method that does not have a try catch, but was called by another method that does have a 'try catch'. If this is the only reason, you can say "i'll add proper try catch". The problem is that in real life you get a real problem, a real log to analyse. When you notice the irrelevant or insufficient method information you don't get the chance to go back to your code, add the try/catch and quickly reproduce the exception. You just end up with missing information to deal with... The current improved methods digs a few frames deep to try and get around this and retrieve the class, line and column in these special situations it could look like this (the trace on the 3rd frame actually returns a valid name and line number):
[1 - 21/11 14:37:50.424914] Exception: System.Threading.Tasks.Task.Delay, Ln 0 Col 0: The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0 or a positive integer less than or equal to Int32.MaxValue. Parameter name: delay [1 - 21/11 14:37:50.425914] Exception: System.Threading.Tasks.Task.Delay, Ln 0 Col 0: The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0 or a positive integer less than or equal to Int32.MaxValue. Parameter name: delay [1 - 21/11 14:37:50.426415] Exception: ScheduleAction.Delay, Ln 156 Col 17: The value needs to translate in milliseconds to -1 (signifying an infinite timeout), 0 or a positive integer less than or equal to Int32.MaxValue. Parameter name: delay

3 Comments

To make the Trace print to a file, add this to your configuation:<system.diagnostics> <trace autoflush="true"> <listeners> <add name="textListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="trace.log" /> <remove name="Default" /> </listeners> </trace> </system.diagnostics>
could you be seeing the wrong method information, because you are using ReflectedType instead of DeclaringType?
Possible. I haven't used it in quite a while (not debugging stuff that justifies these recently)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.