Framework-wide logging extension point. Lives in logger/, no dependencies on retention/ or notifier/. Unlike the notifier, a concrete default (DebugLogger) ships — framework code always gets a non-null logger.
public virtual class AbstractLogger {
public virtual void debug (String source, String message) {}
public virtual void info (String source, String message) {}
public virtual void warn (String source, String message) {}
public virtual void error (String source, String message, Exception thrown) {}
}source convention: originating class name ('QueueRetentionBatch', 'JobQueueRetention', etc.). Keeps grep / filter ergonomic regardless of transport.
LoggerFactory.getInstance().info('QueueRetentionBatch',
'processed ' + scope.size() + ' rows');
LoggerFactory.getInstance().error('NotifierFactory',
'custom notifier threw', caught);LoggerFactory is cached per-transaction — repeated getInstance() calls return the same instance.
Wraps System.debug with the appropriate LoggingLevel and a [source] prefix:
[QueueRetentionBatch] processed 2000 rows (LoggingLevel.INFO)
Error logs additionally include the exception message and stack trace.
public class NebulaLoggerAdapter extends AbstractLogger {
public override void debug(String source, String message) {
Logger.debug('[' + source + '] ' + message);
Logger.saveLog();
}
public override void info(String source, String message) {
Logger.info('[' + source + '] ' + message);
Logger.saveLog();
}
public override void warn(String source, String message) {
Logger.warn('[' + source + '] ' + message);
Logger.saveLog();
}
public override void error(String source, String message, Exception thrown) {
Logger.error('[' + source + '] ' + message, thrown);
Logger.saveLog();
}
}Deploy, then set Logger_Setting__c.ClassName__c = 'NebulaLoggerAdapter'.
Need to mute all framework logs in production? Ship an empty implementation:
public class NullLogger extends AbstractLogger {
// defaults already no-op; no overrides needed.
}Set Logger_Setting__c.ClassName__c = 'NullLogger'.
Logger_Setting__c.ClassName__c |
Returned by LoggerFactory.getInstance() |
|---|---|
| (blank or no record) | DebugLogger |
Resolvable class extending AbstractLogger |
configured instance |
| Unknown class name | DebugLogger (graceful fallback; never throws) |
| Class throws in constructor | DebugLogger (graceful fallback) |
Retention code (JobQueueRetention, QueueRetentionBatch, QueueAdminService) uses LoggerFactory.getInstance() throughout. Existing core framework classes (EventExecutor, the three existing Job* classes, EventQueueActiveRecord) still use raw System.debug — migrating them is tracked in docs/improvements.md as follow-up work.