Building your own plugin
Writing a plugin is: pick the right interface, implement it, register it with a LoggerPlugin__mdt record, ship it.
Which interface
- Trigger plugin (
LoggerPlugin.Triggerable) - runs duringLoggerSObjectHandlerexecution. Use for enrichment, outbound notifications, and per-record automation onLogEntryEvent__e,Log__c,LogEntry__c, and the tag junctions. - Batch plugin (
LoggerPlugin.Batchable) - runs duringLogBatchPurgerexecution. Use for archival, custom purge policies, and metrics.
A plugin implements exactly one of the two.
Example: trigger plugin
Suppose you want to auto-tag every ERROR entry with severity:actionable.
public with sharing class AutoSeverityTagPlugin implements LoggerPlugin.Triggerable { public void execute(LoggerPlugin__mdt configuration, LoggerTriggerableContext input) { if (input.sobjectType != Schema.LogEntry__c.SObjectType) { return; } if (input.triggerOperationType != System.TriggerOperation.BEFORE_INSERT) { return; } for (LogEntry__c entry : (List<LogEntry__c>) input.triggerNew) { if (entry.LoggingLevel__c == 'ERROR') { entry.Tags__c = (entry.Tags__c == null ? '' : entry.Tags__c + ',') + 'severity:actionable'; } } }}Example: batch plugin
Suppose you want to emit a platform event with counts of purged entries.
public with sharing class PurgeMetricsPlugin implements LoggerPlugin.Batchable { private Integer totalPurged = 0;
public void start(LoggerPlugin__mdt configuration, LoggerBatchableContext input) { this.totalPurged = 0; }
public void execute(LoggerPlugin__mdt configuration, LoggerBatchableContext input, List<SObject> scopeRecords) { if (input.sobjectType == Schema.LogEntry__c.SObjectType) { this.totalPurged += scopeRecords.size(); } }
public void finish(LoggerPlugin__mdt configuration, LoggerBatchableContext input) { EventBus.publish(new PurgeMetric__e(TotalPurged__c = this.totalPurged)); }}Register the plugin
Create a LoggerPlugin__mdt record. For the trigger plugin above:
<?xml version="1.0" encoding="UTF-8"?><CustomMetadata xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" fqn="LoggerPlugin.AutoSeverityTag"> <label>Auto Severity Tag</label> <protected>false</protected> <values> <field>IsEnabled__c</field> <value xsi:type="xsd:boolean">true</value> </values> <values> <field>SObjectHandlerApexClass__c</field> <value xsi:type="xsd:string">AutoSeverityTagPlugin</value> </values> <values> <field>SObjectHandlerExecutionOrder__c</field> <value xsi:type="xsd:double">10</value> </values></CustomMetadata>For a batch plugin, populate BatchPurgerApexClass__c instead.
Plugin folder layout
If you’re shipping the plugin as its own package (rather than inline in an existing project), mirror the layout used by the shipped plugins:
nebula-logger/plugins/<name>/ plugin/ <name>/ classes/ # Apex classes (impl + tests) customMetadata/ # LoggerPlugin.<Name>.md-meta.xml objects/ # Any plugin-specific custom objects/fields permissionsets/ # <Name>PluginAdmin.permissionset-meta.xml tests/ # Additional test artifacts (test suite, test permsets) README.mdThe LoggerPlugin.<Name>.md-meta.xml file is the registration record.
Testing plugins
- Write a
<PluginClass>_Tests.clsalongside the implementation underplugin/<name>/classes/. - Add the plugin’s test class to a Logger test suite so
npm run test:apex:suite:<name>covers it. - For trigger plugins: construct a
LoggerTriggerableContextmanually with the trigger records you want to exercise, then callexecute(configuration, input)directly. Do not require a real trigger fire in tests. - For batch plugins: construct a
LoggerBatchableContextand pass a synthesizedscopeRecordslist. Assert on side effects (DML, callouts viaSystem.Test.setMock, published events).
Ordering
- Trigger plugins run after the built-in
LoggerSObjectHandlerlogic for that object. - Multiple plugins on the same handler run in
SObjectHandlerExecutionOrder__corder (nulls last, then byDeveloperName). - Plugins that mutate records (adding tags, populating fields) should run before plugins that publish outbound notifications, so notifications see the enriched data.
New plugin checklist
Before shipping:
- Apex class implements exactly one of
LoggerPlugin.Triggerable/LoggerPlugin.Batchable. LoggerPlugin.<Name>.md-meta.xmlexists, hasIsEnabled__c = true(orfalseif opt-in), and points at the class.<Name>PluginAdmin.permissionset-meta.xmlgrants full access on plugin-owned objects and CMDT.<PluginClass>_Tests.clscovers happy path, empty scope, and disabled-configuration cases.README.mdin the plugin folder documents install steps and any prerequisites.sfdx-project.jsonhas apackageDirectoriesentry for the plugin so it can be built as its own unlocked package.
Where next
- Plugin framework overview - interface details.
- Existing plugins to read for reference: Slack, Big Object Archiving, Log Retention Rules, Async Failure Additions.