-
Notifications
You must be signed in to change notification settings - Fork 7
Logger
The logger offers a generic solution to have one entry point in apex coding for adding log messages. The actual endpoint of where the messages will be going is dynamic. By default all the messages will be send to the System Debug log, but this logic can be extended or replaced entirely.
The log entries can optionally be filtered based on the log level, e.g. Error, Info, Debug. to allow a more granular logging when needed.
The solution is divided into three elements, the logger, log appender and the log formatter, to offer the highest level of flexibility and configurability.
An implementation of the fflib_ILogger
interface will handle every single log entry.
Throughout the apex code this implementation can be referenced to add INFO
,
DEBUG
, WARNING
and ERROR
entries to the log.
The Log Message Formatter will format the log entry into a readable string.
Any implementation from the interface fflib_LogMessageFormatter
can be
utilized to customize the default behavior. Use the
setMessageFormatter(fflib_ILogMessageFormatter formatter)
method to replace
the default implementation.
To offer flexibility in the kind of endpoint that is actually being used to send
the log entries to, the logic is separated via the interface fflib_ILogAppender
.
Any implementation of this interface will accept the formatted log messages
and deliver it to the desired endpoint.
The System.Debug
is used by default when there is no log appender set via
the method setLogAppender(fflib_ILogAppender appender)
.
The setLogAppender
method replaces the default appender,
while the addLogAppender
method add another appender on top of the existing.
Using addLogAppender
will result into multiple log appenders allowing the option
to log a single entry to multiple endpoints.
The Log Appender filter is an abstract class that will act as an filter for
an implementation of fflib_ILogAppender
.
All log entries will be filtered based on three criteria;
1. User defined log level
2. Organisation wide log level
3. Apex (run-time) log level
Whenever an log entry is appended and matches one of these criteria
(greater than or equal), then the entry can be accepted by the appender
by utilising the method Boolean isValidLogLevel(System.LoggingLevel logLevel)
.
The filter can be configured via a hierarchical custom setting named fflib_LogLevelFilterc
.
It only has one field named LogLevelc
containing the log level values:
- NONE
- ERROR
- WARN
- INFO
- DEBUG
The 'Default Organization Level Value' sets the default logging level for the entire Org. To debug a specific user or profile we can create a record and set the required level.
Another way of configuring the filter is by using the setLoggingLevel
method,
however this is primarily used while debugging an issue in realtime.
A Log appender named fflib_LogEventAppender
can be added to the logger in order to
publish Salesforce Platform Events. The benefit of using these events is that they
cannot be rolled back even though and unhandled-exception occurs and Salesforce does
an automatic database rollback.
To enable the Platform Events just add another appender to the logger:
fflib_ILogAppender logAppender = new MyCustomLogAppender();
fflib_ILogger logger = new fflib_Logger().addAppender(logAppender);
Adds a log entry of the type System.LoggingLevel.INFO
with the provided message.
fflib_ILogger logger = new fflib_Logger();
logger.add('My error message);
Adds a log entry of the type System.LoggingLevel.DEBUG
based on the input
provided by the caught exception.
fflib_ILogger logger = new fflib_Logger();
try
{
...
}
catch (Exception e)
{
logger.debug(e);
}
Adds a log entry with the provided message.
fflib_ILogger logger = new fflib_Logger();
logger.debug('My error message);
Adds a log entry of the type System.LoggingLevel.DEBUG
with the provided message.
The String.format method is utilised accepting the args
to complete the message.
fflib_ILogger logger = new fflib_Logger();
String myVar = 'Test';
logger.debug('myVar = {0}', new List<String>{ myVar });
Adds a log entry of the type System.LoggingLevel.DEBUG
based on the input
provided by the caught exception.
fflib_ILogger logger = new fflib_Logger();
try
{
...
}
catch (Exception e)
{
logger.error(e);
}
Adds a log entry of the type System.LoggingLevel.ERROR
with the provided message.
fflib_ILogger logger = new fflib_Logger();
logger.debug('My error message);
Adds a log entry of the type System.LoggingLevel.ERROR
with the provided message.
The String.format method is utilised accepting the args
to complete the message.
fflib_ILogger logger = new fflib_Logger();
String myVar = 'Test';
logger.error('myVar = {0}', new List<String>{ myVar });
Adds a log entry of the type System.LoggingLevel.INFO
with the provided message.
fflib_ILogger logger = new fflib_Logger();
logger.info('My error message);
Adds a log entry of the type System.LoggingLevel.INFO
with the provided message.
The String.format method is utilised accepting the args
to complete the message.
fflib_ILogger logger = new fflib_Logger();
String myVar = 'Test';
logger.info('myVar = {0}', new List<String>{ myVar });
Adds a log entry of the type System.LoggingLevel.WARN
with the provided message.
fflib_ILogger logger = new fflib_Logger();
logger.warning('My error message);
Adds a log entry of the type System.LoggingLevel.WARN
with the provided message.
The String.format method is utilised accepting the args
to complete the message.
fflib_ILogger logger = new fflib_Logger();
String myVar = 'Test';
logger.warning('myVar = {0}', new List<String>{ myVar });
Adds another appender, with a customized implementation of fflib_ILogAppender
,
to the logger allowing multiple endpoint to send log entries.
Replaces the default appender with a customized implementation of fflib_ILogAppender
.
The default virtual implementation of this interface is located at fflib_Logger.LogMessageFormatter
A custom implementation of fflib_LogMessageFormatter
can be added to the logger utility via;
public class MyCustomMessageFormatter implements fflib_LogMessageFormatter
{
...
}
fflib_ILogger logger = new fflib_Logger();
logger.setMessageFormatter( new MyCustomMessageFormatter());
fflib_ILogger logger = new fflib_Logger().setMessageFormatter( new MyCustomMessageFormatter());
This method provides the ability to customize the message before it is send to the Log Appender
Default implementation by fflib_Logger.LogMessageFormatter
is to return the message string unchanged.
This method provides the ability to customize the message before it is send to the Log Appender. The String.format method is utilised accepting the args to format the returned string.
Default implementation by fflib_Logger.LogMessageFormatter
is to return the message using String.format
.
This method provides the ability to customize the message before it is send to the Log Appender. The String.format method is utilised accepting the args to format the returned string.
Default implementation by fflib_Logger.LogMessageFormatter
is to return
the to string converted exception, the Exception.getCause()
and the Exception.getStrackTraceString()
.
The default virtual implementation of this interface is located at fflib_Logger.SystemAppender
,
and is using the System.debug
as endpoint to send the log entries.
A custom implementation of fflib_ILogAppender
can be added to the logger utility via;
public class MyCustomLogAppender implements fflib_ILogAppender
{
...
}
fflib_ILogger logger = new fflib_Logger();
logger.setAppender( new MyCustomLogAppender());
fflib_ILogger logger = new fflib_Logger().setAppender( new MyCustomLogAppender());
Overrides the default Apex logging level. Particular useful while debugging a specific part of the execution context.
fflib_ILogAppender logAppender = new MyCustomLogAppender()
fflib_ILogger logger = new fflib_Logger().setAppender(logAppender);
// some logic
// Let activate a more granular logging level
logAppender.setLoggingLevel(System.LoggingLevel.DEBUG);
// ...
// The code I want to debug
// ...
// And return it back to the default
logAppender.setLoggingLevel(logAppender.DEFAULT_LEVEL);
// other logic
Example implementation of the fflib_Logger
Application.cls Create preferably one single instance of the logger, the ideal location would be the Application class.
public with sharing class Application
{
public static final fflib_ILogger Logger = new fflib_Logger()
.addAppender(new fflib_LogEventAppender())
}
MyClass At any location in the apex code the logger can be referenced from the Application class.
public with sharing class MyClass
{
public void myMethod()
{
Application.Logger.error('An Error Message');
}
}
MyLogEventListener.cls When the fflib_LogEventAppender is used as log appender, then you can create event listeners.
public with sharing class MyLogEventListener extends fflib_LogEventDomain
{
public MyLogEventListener(List<SObject> records)
{
super(records);
}
public run()
{
for(LogEntry logEntry : logEntries)
{
// Some logic to handle the log entries
System.debug(logEntry.loggingLevel, logEntry.message);
}
}
}
fflib_LogEventTrigger.trigger Use only one event listener per trigger so that each listener runs in its own execution context.
trigger fflib_LogEventTrigger on fflib_LogEvent__e (after insert)
{
new MyLogEventListener(Trigger.new).run();
}