Skip to content

Commit

Permalink
Make the logger service more robust to exceptions
Browse files Browse the repository at this point in the history
Added documentation to the public methods of the service
Moved the SimpleLoggerServiceImpl into LoggerServiceImpl in order to make it the default logger
Added a NullLoggerServiceImpl that can be used if all else fails, ensuring that the logger does not fail, even if it doesn't log
Removed needless custom metadata for the logging service
  • Loading branch information
rob-baillie-ortoo committed Apr 5, 2022
1 parent 338f1a7 commit 7c25516
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 35 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,93 @@
/**
* Provides the ability to generate log entries.
* The precise output of the logging will depend on the implementation that is configured.
*
* In all cases, whether a message or exception is sent to the logging implementation is governed by
* the Custom Setting 'Logging Configuration'.
*
* All exceptions from this service are swallowed in order to ensure that a badly configured logger does not
* result in a broken system.
*/
public inherited sharing class LoggerService
{
public enum Level { INFO, WARNING, ERROR }

public static final Level EXCEPTION_LOG_LEVEL = Level.ERROR;

@testVisible
private static Logging_Configuration__c config
{
get
{
if ( config == null )
{
try
{
config = Logging_Configuration__c.getInstance();
}
catch ( Exception e ) // We swallow the exception and create an empty logging configuration because the logger should never throw
// an exception. We just want the default values to apply in the situation that the config can't be loaded
{
config = new Logging_Configuration__c();
}
}
return config;
}
set;
}

/**
* Log the given message at the given level
*
* @param LoggerService.Level The level of the log
* @param String The message to log
*/
public static void log( LoggerService.Level logLevel, String message )
{
if ( ! shouldLog( logLevel ) )
{
return;
}

service().log( logLevel, message );
try
{
service().log( logLevel, message );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when attempting to log ' + message + ' - ' + e );
}
}

/**
* Log the given message at the given level
*
* @param LoggerService.Level The level of the log
* @param String The message to log
* @param Id The Id of the SObject that this log entry relates to.
*/
public static void log( LoggerService.Level logLevel, String message, Id relatedSobjectId )
{
if ( ! shouldLog( logLevel ) )
{
return;
}

service().log( logLevel, message, relatedSobjectId );
try
{
service().log( logLevel, message, relatedSobjectId );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when attempting to log ' + message + ' against ' + relatedSobjectId + ' - ' + e );
}
}

/**
* Log the given exception
*
* @param Exception The exception to log
*/
public static void log( Exception exceptionToLog )
{
if ( ! shouldLogExceptions() )
Expand All @@ -42,49 +106,84 @@ public inherited sharing class LoggerService
service().log( exceptionToLog );
}

/**
* Log the given DmlException
*
* @param DmlException The exception to log
*/
public static void log( DmlException exceptionToLog )
{
if ( ! shouldLogExceptions() )
{
return;
}

service().log( exceptionToLog );
try
{
service().log( exceptionToLog );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when attempting to log ' + exceptionToLog + ' - ' + e );
}
}

/**
* Log the given ortoo_Exception
*
* @param ortoo_Exception The exception to log
*/
public static void log( ortoo_Exception exceptionToLog )
{
if ( ! shouldLogExceptions() )
{
return;
}

service().log( exceptionToLog );
try
{
service().log( exceptionToLog );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when attempting to log ' + exceptionToLog + ' - ' + e );
}
}

/**
* Log the given exception against the given SObject Id
*
* @param Exception The exception to log
* @param Id The SObject to relate this log to
*/
public static void log( Exception exceptionToLog, Id relatedSobjectId )
{
if ( ! shouldLogExceptions() )
{
return;
}

service().log( exceptionToLog, relatedSobjectId );
try
{
service().log( exceptionToLog, relatedSobjectId );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when attempting to log ' + exceptionToLog + ' - ' + e );
}
}

private static Boolean shouldLog( LoggerService.Level logLevel )
{
Logging_Configuration__c config = Logging_Configuration__c.getInstance();

switch on logLevel {
when INFO {
return config.Id != null ? config.Log_INFO_Level_Messages__c : false; // if no config record exists, do not log INFO
return config.Name != null ? config.Log_INFO_Level_Messages__c : false; // if no config record exists, do not log INFO
}
when WARNING {
return config.Id != null ? config.Log_WARNING_Level_Messages__c : false; // if no config record exists, do not log WARNING
return config.Name != null ? config.Log_WARNING_Level_Messages__c : false; // if no config record exists, do not log WARNING
}
}
return config.Id != null ? config.Log_ERROR_Level_Messages__c : true; // if no config record exists, still log ERROR
return config.Name != null ? config.Log_ERROR_Level_Messages__c : true; // if no config record exists, still log ERROR
}

private static Boolean shouldLogExceptions()
Expand All @@ -94,6 +193,15 @@ public inherited sharing class LoggerService

private static ILoggerService service()
{
return (ILoggerService)Application.SERVICE.newInstance( ILoggerService.class );
try
{
return (ILoggerService)Application.SERVICE.newInstance( ILoggerService.class );
}
catch ( Exception e )
{
System.debug( LoggingLevel.ERROR, 'Exception when trying to instantiate the logger - ' + e ); // Logger failure should not stop the system,
// so with nothing else available, instantiate a logger that does nothing
return new NullLoggerServiceImpl();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
/**
* Provides a mechanism for performing a windowed, ordered search against an object as
* defined by the passed searchConfigurationType.
* A simple logger that output all logs to System.debug.
*
* Provides a more thorough and structured output in the log.
*
*/
public with sharing class SimpleLoggerServiceImpl implements ILoggerService
public with sharing class LoggerServiceImpl implements ILoggerService
{
// TODO: should we have a custom metadata record? Or should this be LoggerServiceImpl?
// TODO: should we output more from the standard exceptions?
// TODO: what can we output in the UI? Flag on custom settings to decide
// TODO: check limits for shipping custom settings in managed package
//
public void log( LoggerService.Level logLevel, String message )

public void log( LoggerService.Level logLevel, String message )
{
logMessage( logLevel, message );
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* An implementation of a logger that does nothing.
* This is the fallback logger that is used if the default or configured logger cannot be instantiated
*/
public inherited sharing class NullLoggerServiceImpl implements ILoggerService
{
public void log( LoggerService.Level logLevel, String message ) {} // NOPMD: is a null implementation
public void log( LoggerService.Level logLevel, String message, Id relatedSobjectId ) {} // NOPMD: is a null implementation
public void log( Exception exceptionToLog ) {} // NOPMD: is a null implementation
public void log( Exception exceptionToLog, Id relatedSobject ) {} // NOPMD: is a null implementation
public void log( DmlException exceptionToLog ) {} // NOPMD: is a null implementation
public void log( ortoo_Exception exceptionToLog ) {} // NOPMD: is a null implementation
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>52.0</apiVersion>
<status>Active</status>
</ApexClass>

This file was deleted.

0 comments on commit 7c25516

Please sign in to comment.