Skip to content

Getting Started with Logging

Johannes Fischer edited this page Oct 31, 2023 · 5 revisions

Logging in a Lightning Web Component

Import the log factory using the following import:

import { createLogger } from 'c/rflibLogger';

Then declare a property in your module for the actual logger.

export default class MyModule extends LightningElement {

    logger = createLogger('MyModule');

    ...
}

Last, use the logger to record log statements.

handleSomeEvent(event) {
    // Note the variable length of arguments
    this.logger.info('Event ocurred, {0} - {1}', 'foo', 'bar');
}

Logging in Lightning Component

Insert the wrapper component into any Lightning Component, preferably at the top of the markup.

<c:rflibLoggerCmp aura:id="logger" name="MyCustomContext" appendComponentId="false" />

Then retrieve the logger from your controller or helper code.

({
	doInit: function(component, event, helper) {
		var logger = component.find('logger');

        // Note that second argument has to be a list
        logger.debug('This is a test > {0}-{1}', ['foo', 'bar']);
	}
})

Logging in Apex

Create a logger in your Apex class using one of the following commands:

  1. rflib_Logger logger = rflib_LoggerUtil.getFactory().createLogger('MyContext'); // generally used to create a logger
  2. rflib_Logger logger = rflib_LoggerUtil.getFactory().createBatchedLogger('MyContext'); // used to create a logger in specific situations

Then call the log functions.

logger.debug('This is a test -> {0}: {1}', new List<Object> { 'foo', 'bar' });

Here is a full example for a controller class.

// This will log all Log Events immediately after their creation, depending on their settings.
private static final rflib_Logger LOGGER = rflib_LoggerUtil.getFactory().createLogger('MyController');

@AuraEnabled
public static String doSomething(){
    try {
        LOGGER.info('doSomething() invoked');
        // Application logic here
        return 'Result';
    } catch (Exception ex) {
        LOGGER.fatal('DoSomething threw an exception', ex);
    } 
}

Logging in Apex using the Batch pattern

It's important to note that batch logging should only be used in some rare situations. Please use the standard logger instead for best performance.

When using the batched logging pattern, the code is responsible for the publishing of the log events. This means that it is required to call the rflib_Logger.publishBatchedLogEvents() method at the end of any transaction. It does not matter what logger it is called on as all loggers manage batched log events globally. Batched logging will reduce the number of DML statements, especially for low log level configurations.

Following is an example of an Aura controller using the batch pattern:

// This will log all Log Events as batched events, independent of the settings.
private static final rflib_Logger LOGGER = rflib_LoggerUtil.getFactory().createBatchedLogger('MyController');

@AuraEnabled
public static String doSomething(){
    try {
        // Application logic here
        return 'Result';
    } catch (Exception ex) {
        LOGGER.fatal('DoSomething threw an exception', ex);
    } finally {
        // IMPORTANT: This method must be invoked to trigger the publishing of any queued events. 
        LOGGER.publishBatchedLogEvents();
    }
}

Logging using the Apex Finalizer Interface

Similar to the Logger, the rflib_LogFinalizer implementation is based on dependency injection and comes with a default implementation. Therefore, an instance can be instantiated using the rflib_LoggerUtil class. The following sample code highlights the key elements of a Finalizer implementation.

The rflib_DefaultLogFinalizer will check the ParentJobResult and log an INFO statement if the transaction was successful. If the transaction failed, a FATAL statement with more details will be logged. All log statements that were generated in the Queuable will be included in the Log Event.

public class FinalizerExample implements Queuable {

    private static final rflib_Logger LOGGER = rflib_LoggerUtil.getFactory().createLogger('FinalizerExample');

    public void execute(QueueableContext ctx) {
        rflib_LogFinalizer logFinalizer = rflib_LoggerUtil.createLogFinalizer(LOGGER);

        System.attachFinalizer(logFinalizer);

        LOGGER.info('Foo bar');
    }
}

If you need more work to do in the transaction finalizer other than logging, consider using the Finalplexer created by Chris Peterson, which allows you to attach multiple finalizer to a single transaction.

Logging in Process Builder and Flow Builder

Logging is also supported in Process Builder and Flow using Apex Actions.

In Process Builder, define an Action with the Action Type Apex, give it a unique name and then select the Apex Class Log Message. Fill out the fields to log a message during the Process Builder execution. Please note that there are two optional parameters that can be configured when clicking at the Add Row button at the bottom of the form. The screenshot below illustrates the configuration.

alt text

In Flow Builder, add an Action element to your Flow. When the New Action modal appears, select RFLIB as the category on the left and search for the Log Message action in the right column. Once selected, fill out the standard action fields by giving it a unique name and define your log parameters. Please note that there are two optional parameters that can be configured by enabling the toggle at the bottom of the form. The screenshot below illustrates the configuration.

alt text

Unlike displayed in the screenshots, please avoid using batched logging, it should only be set to true in very rare cases.

Logging Recommendations

Logging is a bit of an art. There is not right or wrong way, so anything described in this section is simply a recommendation based on previous experiences. I hope this will be helpful when writing your code. These are my best practices:

  • Generally use INFO statements
  • Try to create a log "stacktrace", which means that almost every function should have a log statement at the beginning with the arguments
  • Use TRACE statements within loops or for extremely large payloads
  • Every class or lightning component should have a logger instance
  • Use FATAL statements when catching unexpected exceptions in Aura Controller classes
  • Use ERROR statements in service classes, i.e. when catching errors after making a callout
  • Consider using the batch logger for classes dealing with callouts to avoid runtime exceptions created by the log framework
  • Use WARN level for situations that are recoverable, but should not really happen in the real world
  • Reduce log statements by using the formatting feature to easily print multiple variables in a single statement
  • Use rflib_HttpRequest instead of the Apex platform class, this can save hours of debugging integration issues
  • Avoid using batched logging unless it is absolutely necessary. It adds complexity to the code that should not be necessary since Salesforce released dedicated Governor limits for Platform Events
Clone this wiki locally