-
Notifications
You must be signed in to change notification settings - Fork 28
Getting Started with Logging
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');
}
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']);
}
})
Create a logger in your Apex class using one of the following commands:
rflib_Logger logger = rflib_LoggerUtil.getFactory().createLogger('MyContext'); // generally used to create a logger
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);
}
}
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();
}
}
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 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.
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.
Unlike displayed in the screenshots, please avoid using batched logging, it should only be set to true
in very rare cases.
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