19

I have seen a few different flavors of exception logging utility out there, and a common concept is to capture the class and method that were running when the exception was thrown. In the case that made me wonder this, the logger is used for more than just exceptions. It has method signatures like:

public static void logMessage
    (LoggingLevel loggingLevel, String className, String methodName, String message)

It seems to me that we should be able to get the className and methodName parameters programatically. Can it be done? How?

23

My first thought (which does work) is to throw and catch a DmlException (or any Exception type you wish), and use its stack trace string as a starting point.

public class StackTrace
{
    public static String getCurrentString()
    {
        try { throw new DmlException(); }
        catch (DmlException e)
        {
            return e.getStackTraceString().substringAfter('\n');
        }
    }
}

Note the substringAfter('\n'). One of the tricky aspects of this pursuit is that you add calls to the stack in any attempt to write a generic tool to parse the stack trace for you.

While the above code works, upon further inspection it seems the Exception constructor does the work of building the stack trace, and we don't even need to throw. The following method is more concise, works just as well, and consumes less CPU:

public static String getCurrrentString()
{
    return new DmlException().getStackTraceString().substringAfter('\n');
}

So now we know how to get the stack trace. That's all well and good, but we still need to parse it to get the class name and method. Here are some example values:

Class.Demo.Subclass.doStuff: line 18, column 1
Class.Demo.doStuff: line 7, column 1
AnonymousBlock: line 1, column 1

So how do we get the class and method names? Here's one approach:

global static String getClassName(String line)
{
    if (line.startsWith('Class.'))
        line = line.substringAfter('Class.');
    return line.substringBefore(':').substringBeforeLast('.');
}    
global static String getMethod(String line)
{
    return line.substringBefore(':').substringAfterLast('.');
}

And if you want convenience methods to get the currently executing class and method, they would look something like:

global static String getClassName()
{
    return getClassName(new DmlException().getStackTraceString().substringAfter('\n'));
}
global static String getMethod()
{
    return getMethod(new DmlException().getStackTraceString().substringAfter('\n'));
}

Back to the initial problem, how would we integrate this approach into our Logger class? If we try the below approach, we'll log every time that we're in the Logger.smartLog method!

public static void smartLog(LoggingLevel level)
{
    logMessage(level, StackTrace.getClassName(), StackTrace.getMethod());
}

The most efficient and straightforward approach would be to simply not add that call to the stack.

public static void smartLog(LoggingLevel level)
{
    String trace = new DmlException().getStackTraceString().substringAfter('\n');
    logMessage(level, StackTrace.getClassName(trace), StackTrace.getMethod(trace));
}
10
  • 3
    +1 for the exception constructor; also avoid messy debug log pollution with exceptions thrown that can "throw off" the debug log reader with spurious noise – cropredy Dec 27 '16 at 16:19
  • 3
    If the stack trace is set in the constructor, what happens if the exception instance is passed off to another class and thrown there? Seems the trace would be way off. Probably doesn't matter for your purposes. – Daniel Ballinger Dec 28 '16 at 9:36
  • Yeah I wouldn't reuse these exceptions for other purposes. – Adrian Larson Dec 28 '16 at 14:55
  • 2
    Curiously, custom exceptions don't work in the expression return new MyException().getStackTraceString().substringAfter('\n'); -- no stacktrace is returned. I had to use DmlException or StringException or presumably any OOTB exception. – cropredy Sep 8 '17 at 0:15
  • 1
    @ysr I think you missed his point entirely. If you want to parse stack trace to determine calling context, then custom exceptions will not work. – Adrian Larson Jul 18 '18 at 17:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.