A blog dedicated to Salesforce Ohana

Custom Logging Framework in Salesforce

In my current project, I have a requirement where I have to implement a custom logging framework. Salesforce put the debug/exception log with System.debug() statement, but going through that debug log to identify the issue is always a challenge. So the better approach is to store the exception as soon as it occurred somewhere so that later point we can always refer that to identify what happened.

In order to achieve that below is what I did-

First I have created an object(API Name: Custom_Log__c) to store the exception logs-
Below are the custom field inside the object-

  • Class__c(Text Area(255))   From which class the exception is coming
  • Method__c(Text Area(255))  From which method the exception is coming
  • Message__c(Long Text Area(32768))  Stack trace of the exception
  • Priority__c(Picklist) - Possible values are DEBUG, ERROR, FATAL, INFO, WARNING
Now I need to create the below custom settings -
EnableCustomLogging with possible values true/false. 
The reason behind is that with EnableCustomLogging = true, exceptions will be stored in the custom object i.e. Custom_Log__c and with EnableCustomLogging = false, exception will be stored in the debug log with System.debug() 

Now with this setup, I have created the visual page.
Source Code:
<apex:page controller="CustomLogController">
    <apex:form >
        <apex:commandButton value="Divide By Zero Exception!" action="{!divideByZeroException}" />
        <br/>
        <apex:commandButton value="Null Pointer Exception!" action="{!nullPointerException}" />
    </apex:form>
</apex:page>
Now the controller - CustomLogController:
public class CustomLogController {
    public void divideByZeroException(){
        Integer number1, number2;
        try{
            number1 = 10;
            number2 = 0;
            Integer number3 = number1/number2;    
        }catch(Exception ex){
            String message = 'Error: ' + ex.getMessage() + '; Stack Trace:' + ex.getStackTraceString();
            CustomLogging.logMessage('CustomLogController', 'divideByZeroException', message, CustomLogging.WARNING);
        }
    }
    
    public void nullPointerException(){
        Contact aContact = null;
        try{
            String email = aContact.Email;
        }catch(Exception ex){
            String message = 'Error: ' + ex.getMessage() + '; Stack Trace:' + ex.getStackTraceString();
            CustomLogging.logMessage('CustomLogController', 'nullPointerException', message, CustomLogging.ERROR);
        }
    }
}
Now the CustomLogging class-
public class CustomLogging {
    
    public static String INFO = 'INFO';
    public static String DEBUG = 'DEBUG';
    public static String WARNING = 'WARNING';
    public static String ERROR = 'ERROR';
    public static String FATAL = 'FATAL';
    
    private static Boolean isEnabled(){
        Decision_Object__c enableCustomLogging = Decision_Object__c.getInstance('EnableCustomLogging');
        System.debug('IsEnabled: ' + enableCustomLogging.Value__c);
        return enableCustomLogging.Value__c;
    }
    
    public static void logMessage(String className, String methodName, String message, String Priority){
        if(isEnabled()){
            Custom_Log__c newLogMessage = new Custom_Log__c(
                Class__c = className,
                Method__c = methodName,
                Message__c = message,
                Priority__c = Priority);
            try{
                Database.insert(newLogMessage);
            }catch(Exception ex){
                System.debug(
                    'Failed to INSERT the [Apex Debug Log] ADL record. ' +
                    'Error: ' + ex.getMessage()
                );
            }
        }else{
            String completeErrorMessage = 'Error occured at class: ' + className + ' method: ' + methodName + 
                ' and the error is: ' + message;
            System.debug(completeErrorMessage);
        }
        
    }
}
And finally the test class - This is very important
@isTest
private class UnitTestCustomLogController {
    
    private static void setEnableCustomLoggingFlag(Boolean value){
        insert(new Decision_Object__c(Name='EnableCustomLogging', Value__c=value));
    }
    
    static testMethod void testWhetherCustomLoggingIsGettingCreatedWhileDisabled() {
        setEnableCustomLoggingFlag(false);
        CustomLogController customLogController = new CustomLogController();
        customLogController.divideByZeroException();
        
        Integer numberOfCustomLogs= [Select count() From Custom_Log__c];
        System.assertEquals(0, numberOfCustomLogs);
    }
    
    static testMethod void testDivideByZeroException(){
        setEnableCustomLoggingFlag(true);
        CustomLogController customLogController = new CustomLogController();
        customLogController.divideByZeroException();
        
        Integer numberOfCustomLogs= [Select count() From Custom_Log__c];
        Custom_Log__c customLog= [Select Class__c, Priority__c, Method__c, Message__c From Custom_Log__c LIMIT 1];
        System.assertEquals('CustomLogController', customLog.Class__c);
        System.assertEquals('divideByZeroException', customLog.Method__c);
        System.assertEquals(CustomLogging.WARNING, customLog.Priority__c);
    }
    
    static testMethod void testNullPointerException(){
        setEnableCustomLoggingFlag(true);
        CustomLogController customLogController = new CustomLogController();
        customLogController.nullPointerException();
        
        Integer numberOfCustomLogs= [Select count() From Custom_Log__c];
        Custom_Log__c customLog= [Select Class__c, Priority__c, Method__c, Message__c From Custom_Log__c LIMIT 1];
        System.assertEquals('CustomLogController', customLog.Class__c);
        System.assertEquals('nullPointerException', customLog.Method__c);
        System.assertEquals(CustomLogging.ERROR, customLog.Priority__c);
    }
}
Done. That's all.
So now if I click on any of the buttons in VisualForce page and EnableCustomLogging = true, I can see one record is getting inserted into the Custom_Log__c object like below-
If you have any feedback, please share. Thanks.

Share:

No comments:

Post a Comment

Follow Me

Enter your email address:

Delivered by FeedBurner

Popular Posts

Labels

Salesforce (105) Apex (44) admin (27) ADM (20) visualforce (20) dev 501 (19) integration (18) learn salesforce (18) 501 (16) SOAP (13) lightning (12) tutorial (11) Certification. (9) javascript (8) Trigger (7) test class (7) unit testing (7) Sharing and Visibility (6) design pattern (6) report (6) security (6) trailhead (6) Advanced Admin (5) Certification (5) Kitchener Developer Group (5) New Features (5) SOQL (5) css (5) dashboard (5) debug (5) developer (5) formula (5) mobile (5) salesforce release (5) service cloud (5) solution management (5) use case (5) JSON (4) Lightning Experience (4) Salesforce DX (4) WebSphere (4) best practice (4) cast iron (4) component (4) deployment (4) github (4) html (4) polymer (4) profiles (4) responsive (4) tdd (4) ui (4) Advanced Apex (3) Architect (3) Live Chat (3) Online Event (3) Performance (3) Products (3) Role (3) Sales Cloud (3) Scratch Org (3) Study Notes. (3) Summer15 (3) Tips (3) Web Technology (3) dynamic apex (3) event (3) license (3) map (3) mapbox (3) singleton (3) version controlling (3) Bulkify (2) Data Architecture and Management Certification (2) Devops (2) Distributed Version Controlling (2) ES6 (2) Eclipse (2) Einstein (2) Enterprise Territory Management (2) Financial Services Cloud (2) Force.com IDE (2) Governor Limit (2) Groups (2) IBM (2) Implicit Sharing (2) JourneyToCTA (2) Kitchener User Group (2) Lightning Design System (2) Live Agent (2) Metadata (2) Opportunity (2) Price Book (2) REST (2) SOSL (2) Sharing (2) Spring 15 (2) Summer17 (2) Territory (2) ant (2) automation tool (2) basic (2) chatter (2) coding (2) communication (2) console (2) controller (2) documentation (2) flow (2) git (2) jquery (2) logging (2) object (2) permission (2) process builder (2) release (2) salesforce1 (2) strategy (2) xml (2) Action Plan (1) Action Plan Template (1) Agent Productivity (1) Analytics (1) Apex Sharing (1) Arrow (1) Asynchronous callout (1) Aura Framework (1) Bots (1) Browser (1) Bulk data load (1) CTA (1) Calendar (1) Canon (1) Case Management (1) Cheat Sheet (1) Classic (1) Community (1) Constructor (1) Contact Center (1) Continuation (1) Continuous Integration (1) Convert (1) Cookie (1) Custom Metadata (1) Custom Object (1) Customer (1) Decorator Design Pattern (1) Dev Hub (1) Diwali (1) Email (1) FSC (1) Function (1) Goals (1) Guide (1) Household (1) Ideas (1) Improvement (1) KPIs (1) Large Data Volume (1) LastModifiedDate (1) Lightning Web Component (1) Manual Sharing (1) Metrics (1) New (1) OOPS (1) OWD (1) Omni-Channel (1) Partner (1) Person Account (1) Photo (1) Pipeline (1) Platform Developer I (1) Presentation (1) Product Schedule (1) Profile (1) Promise (1) Prototype (1) Public Site (1) Query Plan (1) QuickReference (1) Reports (1) Retrieve (1) Role Hierarchy (1) SFDX (1) Salesforce Optimizer (1) Session (1) Sharing Rule (1) Sharing Sets (1) Site (1) Skills (1) Snap-ins (1) Spring 17 (1) Summer14 (1) Summer16 (1) Switch (1) SystemModStamp (1) User License (1) Users (1) Webservice (1) Winter'15 (1) Winter'17 (1) access (1) agile (1) app (1) approval process (1) aura (1) awesome (1) backup (1) bitbucket (1) book (1) campaign (1) change set (1) code (1) code coverage (1) configuration (1) csv (1) custom button (1) custom settings (1) customization (1) data loader (1) database (1) delegate Admin (1) describe (1) dom (1) dreamforce (1) duplicate (1) dynamic (1) equals (1) error (1) field-level security (1) folder (1) ftp (1) generic (1) gift (1) global describe (1) hashcode (1) import wizard (1) jenkins (1) keynote (1) long running requests (1) monitoring (1) mysql (1) page layout (1) personal (1) power of one (1) record type (1) relationship (1) request (1) review (1) sub-tab (1) tab (1) username (1) visual workflow (1) workflow (1)

Total Subscribers

Total Pageviews