4

I've set out to build a custom error handling and logging feature for my org wherein all apex exceptions will be logged into a custom sObject for analysis and follow-up.

public class TriggerException extends Exception {

    public TriggerException(String message, Exception ex, String className, String methodName) {
        this(message, ex);
        ExceptionLog__c log = new ExceptionLog__c(
            ClassName__c = className,
            MethodName__c = methodName,
            ErrorLine__c = errorLine,
            ErrorMessage__c = errorMessage,
            ErrorDetails__c = errorStackTrace,
            ErrorType__c = errorType
        );
        insert log;
    }

}

However, this doesn't ever seem to save the ExceptionLog__c and I'm assuming it's because it's also caught up in the same database rollback procedure as the rest of the code...

How can I do this without having to add UtilClass.logMe(...); to every catch {} block in my org??

2
  • 2
    Huh. I thought there was an idea like that on the IdeaExchange, and I found... I posted it. Sadly, it's received no votes.
    – sfdcfox
    Commented Jun 23, 2015 at 1:25
  • Well, I added a vote for it. I hope they take a look at it one of these days! Commented Jun 23, 2015 at 16:27

2 Answers 2

1

Consider using an immediate Platform Event to generate log records. This has the huge benefit of persisting your logs even if an exception rolls back the transaction.

Another slick modification would be to auto-generate the context (class, method, line, etc) by using an exception. This way the caller doesn't need to type those parameters in, and maintenance is much easier. You could put these methods in a "Util" class

public static String getContext() {
  String className = 'Unknown';
  String methodName = 'Unknown';
  Integer line = 0;

  final String stacktraceLine = new DmlException().getStackTraceString()
    .substringAfter('\n').substringBefore('\n'));

  Matcher m = STACK_TRACE_PATTERN.matcher(stacktraceLine);
  if (m.find()) {
    final Boolean hasGroup3 = String.isNotBlank(m.group(3));
    className = m.group(1) + (hasGroup3 ? '.' + m.group(2) : '');
    methodName = m.group(hasGroup3 ? 3 : 2);
    methodName = String.isBlank(methodName) ? '(unknown method)' : 
      methodName.replace('<init>', '(ctor)').replace('__sfdc_', '(get/set) ');
    line = Integer.valueOf(m.group(4));
  }    

  return className + '.' + methodName + ':' + line;
}

I recently posted the Apex BOOST Library to App Exchange if you'd like to try some of this out.

1
  • Platform Events have become hugely useful since! I had not gone back and applied it to this application, thanks for the side project this week! Great suggestion! Commented Dec 29, 2022 at 13:46
4

I would not suggest you to insert record everytime in method (Code not bulkified), If this method is called in loop, You may run into DML governor limit and therefore exception handling will cause exception.

Here, is sample utility class [pseudocode]

public class HandleException
{
private static list<ExceptionLog__c> lstException = new list<ExceptionLog__c>();

public void addException (String message, Exception ex, String className, String methodName)
{
    ExceptionLog__c log = new ExceptionLog__c(
        ClassName__c = className,
        MethodName__c = methodName,
        ErrorLine__c = errorLine,
        ErrorMessage__c = errorMessage,
        ErrorDetails__c = errorStackTrace,
        ErrorType__c = errorType
    );
    lstException.add(log); 
}

public static void saveExceptionLog()
{
    if(!lstException.isEmpty())
    {
        Database.insert(lstException,false); 
    }
  }
 }

You should call method saveExceptionLog() at end of your class or Trigger, If there is any unhandeled exception then even this approach will not work. We have to add try and catch block at all possible places.

Once object can have around 25 lookup fields, If you want more granular logging then you can create lookup for record where it occurred. This is very helpful for end to end reporting purpose and to reproduce issue with Data.

4
  • That's a fair point about bulkification, and a good reason to not handle it within the Exception class itself... I will go back to the failover way of catch (Exception e) {log(e)} Commented Jun 23, 2015 at 16:30
  • @jitendra zaa do we have to call both the static methods inside the catch block ?
    – Jarvis
    Commented Dec 11, 2015 at 1:53
  • 1
    @Bennie If you have multiple catch blocks, then call addException() methods in all catch block and at end of transaction, call saveExceptionLog() as it uses DML. Calling multiple time may adversely effect DML Commented Dec 11, 2015 at 18:47
  • @JitendraZaa What if I use a future method to write a custom object that could catch the error details. As future method will have more resources we can avoid calling these two separate methods for logging and saving exceptions in each class.
    – Jarvis
    Commented Jan 27, 2016 at 6:53

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .