• Services
      • Managed Services
      • Strategic Services
      • Implementation
      • Optimization
      • System Expansion
      • Custom Development
      • Training
  • Technology
      • Partners
      • Salesforce
      • Pardot
      • Marketo
      • Salesforce CPQ
  • Knowledge Center
      • Clients
      • Case Studies
      • Blog
  • About
      • Leadership
      • Advisors
      • Careers
      • Contact

For Developers

January 3, 2018

Lightning Exception Handling Framework

John Pipkin

There are 3 types of code: Bad code, functional code, and good code. Bad code simply doesn’t work and functional code works but has a bad user experience, but what makes good code “good”? There are a million ways to correctly answer that question, but I’m not writing on a million topics so I will point out a select few. Good code handles exceptions with grace and doesn’t repeat itself over and over (DRY method). In my last blog I talked about the Lightning Lookup input field. For this blog, I will be covering how to handle Lightning exceptions with code reusability.

Backstory

Over the past year of developing more and more on the LEX framework, I have noticed that I never found a good exception handling framework that I could use for my custom apps and components. I found myself writing the same exception handling code for each component individually and they all varied to a small degree. Now, I do not claim to be an expert by any means but I wanted to share my exception handling framework I have designed with anyone who is currently on the same journey as I was.

The Framework

The framework consists of 2 different concepts: Handling exceptions in Lightning and notifying developers or teams when certain exceptions are raised with detailed information on what happened. Having the right information, as you may know, speeds up the debugging process so application downtime is shortened.

The Component

This framework uses an extensible component that any custom component can extend. This component contains all of the server callback response handing and exception handling as well as a few exposed helper methods. Let’s take a look at the component.

<aura:component extensible="true" access="GLOBAL" controller="Log">
	<!-- global attributes -->
    <aura:attribute name="devicetype" type="String" access="GLOBAL"/>
	<aura:attribute name="deviceinfo" type="DeviceInformation" access="GLOBAL"/>
	<aura:attribute name="emailOnError" type="Boolean" default="TRUE" access="GLOBAL"/>

	<!-- init handler -->
	<aura:handler name="init" value="{!this}" action="{!c.init}"/>


	<!-- Global methods -->

	<aura:method name="successfulResponse" access="GLOBAL">
		<aura:attribute name="res" type="Object" required="true"/>
	</aura:method>

	<aura:method name="success" access="GLOBAL">
		<aura:attribute name="msg" type="String" required="true" access="GLOBAL"/>
		<aura:attribute name="mode" type="String" required="false" access="GLOBAL"/>
	</aura:method>

	<aura:method name="error" access="GLOBAL">
		<aura:attribute name="msg" type="String" required="true" access="GLOBAL"/>
		<aura:attribute name="mode" type="String" required="false" access="GLOBAL"/>
	</aura:method>

	<!-- Extending component body -->
	<div>{!v.body}</div>

	
</aura:component>

This component has a few attributes and methods that any other component that extends this can use.

  • Attribute: deviceinfo
    • Description: DeviceInformation wrapper instance with user device information (Browser, OS, Desktop/Mobile, etc)
  • Attribute: emailOnError
    • Description: Determines if a notification email is sent if this component (only this component) fails to parse device information. Defaults to TRUE
  • Method: successfulResponse(res)
    • Description: parses the response from a server callback. “res” is the response object.
  • Method: success(msg,[mode])
    • Description: Fires a “Success” toast with the given message
  • Method error(msg,[mode])
    • Description: Fires a “Error” toast with the given message

The Log

I had a client last year that wanted a comprehensive logging application that would notify different departments of Apex errors based on what application the error came from. What we came up with was extremely useful for proactively dealing with and fixing application exceptions and minimizing the amount of downtime for the business and the customers. This framework implements a simplified, yet just as effective, version of that solution. Here are the details of the Log class:

public class Log {
    public static void log(String msg) // stores debug statement and prints to the debug log
    public static void log(String msg, Object o) // same as above. The Object, o, is serialized as part of the debug statement
    public static void notify(Object obj, String appName, DeviceInformation lla) // gathers exception data and sends an email 
    public static void notify(String appName, String emailbody, String subject) // just send an already constructed email
}

The “log” method is used for printing to the debug log (like using System.debug()) but it also stores that log entry for the email notification if an exception occurs. Adding the right debug statements in your code can help you know exactly why the exception occurred without further investigation.

In the method, notify(Object obj, String appName, DeviceInformation lla), the first variable in the method signature is a general object. It explicitly parses Exception, List<Database.SaveResult>, and List<Database.UpsertResult> objects, but it will also work with any type that is serializable (unlike SelectOption class, which cannot be serialized). The appName variable is used to query a custom metadata type called, “Log Notification Settings.” This metadata type uses MasterLabel as the appName and there is a field, “Email On Error”, that takes a comma-separated list of emails to email the exception details to. In the following example, I am using “General” as the appName.

The “notify” method should be used inside a catch block to gather the exception information and send the email.

Implementation

Now that we are somewhat familiar with what the design is using, let’s take a look at how to implement with a custom component.

<aura:component implements="flexipage:availableForAllPageTypes" extends="c:LightningLog" controller="ExampleLightningLogCmpController">

	<aura:set attribute="notifyOnError" value="TRUE"/>

	<div>Device: {!v.devicetype}</div>

	<div>
		<lightning:button variant="brand" label="Click Me!" onclick="{!c.doSomething}"/>
	</div>
</aura:component>

To extend the logging component to a new custom component, all that is needed is to add extends=”c:LightningLog” to the aura:component markup. Once that is in place, we now have access to the logging functionality in this new component.

As you can see in line 3-5, we have access to the log component’s attributes. If you need to override an attribute, like I am on line 3, you must use aura:set.

Let’s take a look at what the “Click Me!” button will do.

doSomething : function(component) {
	var action = component.get('c.doIt');

	action.setParams({
		'lla' : JSON.stringify(component.get('v.deviceinfo'))
	})
	action.setCallback(this,function(result){
		if(!component.successfulResponse(result)){
			console.log('error');
			return;
		}
		component.set('v.deviceinfo',result.getReturnValue());
		console.log(result.getReturnValue());
	});

	$A.enqueueAction(action);
}

In this method, we are calling a server-side controller method and setting the result to the log component’s “deviceinfo” attribute. For the action parameters, I am passing in the DeviceInformation as a JSON string. I am doing this because I would like the device information to be in the exception email. Since the DeviceInformation is an Apex wrapper, the object needs to be serialized in order to be sent to the server. DeviceInformation has a static method to help you deserialize once inside the Apex method.

DeviceInformation llaObj = DeviceInformation.deserialize(lla);

Here’s the super cool part: Notice on line 8 that we are handling the server response in 1 line by calling the component.successfulResponse(res) method! The successfulResponse method returns a boolean (true for successful and false for errors). Having this response parsing and exception handling centralized helps keep the code clean, maintainable, and scalable.

Finally, here’s the Apex controller method that uses the framework:

@AuraEnabled
public static DeviceInformation doIt(String lla){
	Log.log('lla\n',lla);
	DeviceInformation ret = DeviceInformation.deserialize(lla);
        Savepoint sp = Database.setSavePoint();
	try{
		
		Log.log('query for account');
		Account a = [Select Id,Industry from Account LIMIT 1];
		Log.log('account\n',a);
		a.Name = 'Test 1234';
		update a;
	}
	catch(Exception e){
                Database.rollback(sp);
		ret.auraerror = e.getMessage();
		Log.notify(e, 'General', ret);
	}
	return ret;
}

Please note that if you throw an AuraHandledException object inside the catch block, the email queue will be rollbacked and no notification will be sent.

The important piece of this is the DeviceInformation variable, “auraerror”. Setting the error message to this variable and not raising an exception will allow for the user to see the error message and the exception email to be sent. This doesn’t only work for DeviceInformation class. Any class that has an @AuraEnabled variable named “auraerror” can be returned from the server and parsed in the log component. For example:

public class CanParse {
    @AuraEnabled String auraerror;
    @AuraEnabled Boolean anotherproperty;
}

public class CanNotParse {
    @AuraEnabled String returnString;
}

The class, CanParse, is able to be read with the LightningLog component because it has the variable “auraerror” that is visible to the lightning component. CanNotParse does not have that variable so the LightningLog component will simply ignore it.

Lightning and Beyond

The Log apex class is written so that it can be used outside of Lightning components as well. Just add the Log class methods to any Apex application.

Download

I hope this component will be a great asset in your lightning component library! You can find the code on GitHub but we  can also help with your coding and custom development needs.

Happy Coding!

Share this story:

  • Tweet

About the Author

John Pipkin

John is a Senior Developer at OpFocus. John specializes in Apex, Visualforce, integrations, jQuery, Visual Flows, and Lightning Processes, but particularly enjoys the power to customize pretty much anything on the Salesforce platform. When he is not working his magic on the Force.com platform, John enjoys creating musical magic, and plays the drums and guitar. Since he's a total rock star, John has 11+ Salesforce Certified designations. Go through this list if you want to feel immensely inadequate about your Salesforce skills... Administrator (201), Platform App Builder, Developer (401), Platform Developer I, Sales Cloud Consultant, Service Cloud Consultant, Community Cloud Consultant, Mobile Solution Architecture Designer, Integration Architecture Designer, Development Lifecycle & Deployment Designer, Identity & Access Management Designer, and System Architect certifications.

Related Posts

October 18, 2018

Overriding the Opportunity Contact Role in Salesforce Lightning

Brenda Finn
July 6, 2018

Apex Sharing Gotchas in Salesforce

Brenda Finn
January 3, 2018

Lightning Exception Handling Framework

John Pipkin

Leave a Comment

Click here to cancel reply.


Sign up for weekly OpFocus blog updates!

  • Navigate

    • Services
      • Managed Services
      • Custom Development
      • Strategic Services
      • Data & Integration
      • Implementation
      • Training
    • Technology
      • Salesforce
      • Pardot
      • Marketo
      • Salesforce CPQ
    • Knowledge Center
      • Clients
      • Blog
    • About
      • Leadership
      • Advisors
      • Careers
      • Contact

    Office Locations

    Global Headquarters
    78 Blanchard Road
    Suite #203
    Burlington, MA 01803
    (781) 214-7440 info@opfocus.com

    Canada
    1 Yonge Street,
    Suite 1203
    Toronto, ON M5E 1W7
    (416) 848-0490 info@opfocus.com

    LinkedIn
    Twitter
    Instagram
    Facebook
    YouTube

    OpFocus, Inc.

  • © 2006 - 2019 OpFocus All Rights Reserved

    Privacy Policy | Terms of Use

  • We use cookies to make interactions with our website and services easy and meaningful. To better understand how they are used, you can read more about our cookie policy. By continuing to use this site you are giving us your consent to do this.