Logging Exceptions in Salesforce

Logging
Exceptions
in Salesforce

Exceptions in Salesforce

Unpredicted behavior in a custom code. Can we eliminate it?

The ability to customize your Salesforce org code is not just a “nice to have.”  It greatly increases the capability and flexibility of Salesforce.

However, custom code can also be tricky to use. It would be great if we could detect unpredicted behavior in our custom code through functionality, such as catching and logging exceptions.

Lightning components

There is no problem with logging exceptions in Lightning Components. JavaScript code can be surrounded in a try catch block. As a result we can easily track any type of client side exceptions:

doSomething: function(component) {
    try {
        unexistingMethod(); // ReferenceError here
    } catch(e) {
        // log exception here
}
}

It is up to you what to do with such errors. It can be JavaScript console output:

console.error(e);

or Lightning notification to user:

var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
    "title": e.name,
    "message": e.message,
    "mode": "sticky",
    "type": "Error"
});
toastEvent.fire();

In the case where some exceptions happen at a server Apex controller, we are also on a safe side through the use of a special type of exception – AuraHandledException.  We can convert any exception into an AuraHandledException and send it to Lightning Component as a response:
@AuraEnabled

public static String serverMethod(String name) {
    try {
        // some Apex code
    } catch(DMLException ex) {
throw new AuraHandledException('Unable to save record');
}
}

Please, note, AuraHandledException supports String argument only. This class is not extensible. If you need to send some data to client side use the following approach:

  • create Apex class to hold the data.
  • serialize it as JSON.
  • pass it to the AuraHandledException.
  • deserialize it on the client side and work with data as an object.

Migrate from Aura to Lightning Web Components to Increase PerformanceExceptions Salesforce

Apex

The situation with a pure Apex code is slightly more complicated. We can set up an Apex Exception Email that will send an unhandled exception to an Administrator’s mailbox. However, in many cases this is not enough. Here are some of the disadvantages,

  • Email context is not configurable. It contains an Apex stack trace, Org ID and User ID.
  • Exceptions from all managed packages will be sent. There is no way to subscribe to a specific application.
  • We are unable to make automatic responses in case of such exceptions.

Let’s overview the tools we can use in Salesforce to resolve it.

Get 100% Code Coverage for Salesforce Custom Metadata Based Decisions

Rollback of execution context

Salesforce has a unique feature to commit any transaction to a database automatically. The System will persist changes after the execution context is finished if no unhandled exception occurs.

This approach is handy, and in most cases it is exactly what you want.  But if an unhandled exception occurs, it rolls back everything, not only DML operations. Scheduled jobs, future methods, batches, queueable jobs, emails will not be run or sent.  That is why the following piece of code will never work:

public void someMethod() {
try {
// some Apex code
} catch(Exception ex) {
// log exception here
throw ex;
// rethrow exception again
}
}

By rethrowing an exception, you add an unhandled exception into the execution context.

Do we need to catch all exceptions?

The simple answer is no. For example, you are doing a complex validation in an Apex trigger. Any call of addError() method is a DMLException. We are not interested in catching such exceptions.

The best practice is to catch exceptions in a top level component, e.g., Web service, controller of Visualforce page, server side controller of Lightning Component, etc. In other words, in any place where an execution context will start.

Adding a try .. catch block to all of your methods can ruin the readability of your code. It also increases the complexity of writing unit tests.

What is more important, by catching exceptions you are braking the logic of your application. You are forcing low level components to be responsible for decisions, e.g., what to do in a critical situation.

From time to time you can also get an exception you would never expect. For example, while your code is sending email to Contact, the following is received:

System.EmailException: INVALID_EMAIL_ADDRESS, email address has bounced

Such things are really hard to predict, and the reason we must be able to log exceptions.

Logging exceptions

Let’s imagine we want to log an exception in a custom Apex code. For this purpose we can use a custom object to store such exceptions. Let’s say ErrorLog__cobject. Look at the code below:

public static void someMethod(String name)
try {
// Apex code here
} catch(Exception ex) {
ErrorLog__c log = new ErrorLog__c(
Description__c = 'Error: ' + ex.getMessage()
);
insert log;
}
}

This code will work if the execution context is clean, i.e., when there is no unhandled exception. However, if this code is a part in a chain of the execution context, it might not work. Look at the diagram below:Apex code execution

If an exception occurs after inserting the error log record into a database, the entire transaction will be rolled back. Your ErrorLog__c record will not be saved.

Logging system with platform events

The situation has totally changed with Platform Events. It is a new feature generally available since the Summer ’17 release (API version 40.0). Platform Event is a bus to send read only messages. When the message is sent, you are not able to return it.  We will use this feature in the error logging system.

The idea is simple. When you need to save a new ErrorLog__c record, publish a Platform Event.  Later, you can subscribe to this event and save the ErrorLog__c record in the database:Apex code platform events

Please, note that the Platform Event trigger runs under the Automated Process entity. This is a system user. In order to see which user has sent the event, have a look at CreatedBy field on the Platform Event record.

What is more important, we are not limited in logging the exception. We can react to it. For example, we can run the Apex method to repeat the failed operation as well as many more uses.

Final thoughts

Platform Events is a great tool for an event-driven software architecture. They extend programming functionalities in Apex, and can be used even in an execution context with an unhandled exception.

Avenga is here to help our clients and partners actualize the Salesforce® investment into quality deployment, ecosystem integration, timely project implementation.

Other articles

or

Book a meeting

Zoom 30 min

or call us+1 (800) 917-0207

Start a conversation

We’d like to hear from you. Use the contact form below and we’ll get back to you shortly.