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