Capture Apex Errors in Salesforce Objects
What do you do if you want to capture exceptions in Apex? Since Apex is a Java-like language, your gut instinct will be to do something like this -
Id inAcctId = '0011J00001mZTMsQAO';
try {
Integer someNum = 1;
if (someNum == 1) {
throw new IllegalArgumentException('I err ' + Datetime.now());
}
}
catch (exception e) {
System.debug('message: ' + e.getMessage());
FeedItem msg = new FeedItem(ParentId = inAcctId, Body = e.getMessage());
insert msg;
}
.. and this totally works.
- Apex sees exception since
someNum == 1
- Log Chatter message
- End of everything
But, what if you want to “raise the exception” to caller. For e.g. you want to throw a 500 error to the system calling Apex or to the front-end Lightning component?
Following the above approach will “suppress” the error - caller does not see the error message.
An easy way to raise the exception back to caller is to “re-throw” the messsage in catch
.
Id inAcctId = '0011J00001mZTMsQAO';
try {
Integer someNum = 1;
if (someNum == 1) {
throw new IllegalArgumentException('I err ' + Datetime.now());
}
}
catch (exception e) {
System.debug('message: ' + e.getMessage());
FeedItem msg = new FeedItem(ParentId = inAcctId, Body = e.getMessage());
insert msg;
throw new AuraHandledException(e.getMessage());
}
This code, while perfectly valid, does not seem to log any kind of message!
- Apex encounters exception
- Go to
catch
- Log Chatter message
- Process another custom exception
- Salesforce rolls-back transactions because of exception
- Exception to caller
The error message is “undone” as an unexpected side-effect of transaction roll-back.
There are two options if you want to capture error messages in Salesforce, but want to throw an exception to caller -
- Change the way errors are handled. You could capture error in
catch
, set anerror
string in return message and return to caller. Caller processes error based onerror
return string - Use an async service to log error - platform events can execute without problems even when the caller errors out.
I prefer option (2) since it is cleaner and allows us to standardize exception handling across caller/callee. But, one needs to be weary about the async call limits.
Using Platform Events for Exception Handling #
Create a platform event -
- Go to
Setup
>Integrations
>Platform Events
- Create a new event
Error Log
- Specify custom logging fields you want to capture
Next, invoke this platform event from custom code -
Id inAcctId = '0011J00001mZTMsQAO';
try {
Integer someNum = 1;
if (someNum == 1) {
throw new IllegalArgumentException('I err ' + Datetime.now());
}
}
catch (exception e) {
System.debug('message: ' + e.getMessage());
List<crmcog1__Error_Log__e> errLog = new List<crmcog1__Error_Log__e>();
errLog.add(new crmcog1__Error_Log__e(Message__c=e.getMessage(), Code__c='42'));
List<Database.SaveResult> res = EventBus.publish(errLog);
throw new IllegalArgumentException('I err in err ' + Datetime.now());
}
Subscribe to this event in Work Bench to see the message -
You can use the platform event to create records in custom object, log messages to Chatter, or do anything else as you please.