Skip to content

Commit

Permalink
Logger Performance Improvements (#660)
Browse files Browse the repository at this point in the history
* Benchmarking & performance improvements for transactions with many log entries

* Removes duplicated field truncation from LoggerDataStore's pre-platform event publish responsibilities

* Further optimizations - strip out Logging classes prior to entering LoggerStackTrace, saving an average of 3 seconds processing time over 500 log entries

* Adds PMD7 suppressions where applicable

* Standardizing how timestamps are added to and maintained within LogEntryEventBuilder with feedback from @jongpie

* Code review feedback from @jongpie - changed LoggerParameter.StoreApexHeapSizeLimit to LoggerParameter.StoreHeapSizeLimit, standardized referring to the limit in the singular as opposed to plural, and added two more tests: 1) LogEntryEventBuilder_Tests.it_should_not_set_transaction_heap_limits_fields_when_transaction_limit_tracking_is_disabled_via_logger_parameter(), and 2) LoggerBenchmarking_Tests.it_benchmarks_without_setting_heap_limit()
  • Loading branch information
jamessimone committed Mar 29, 2024
1 parent b3a08f0 commit 6453b17
Show file tree
Hide file tree
Showing 28 changed files with 512 additions and 620 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@

The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.

## Unlocked Package - v4.13.4
## Unlocked Package - v4.13.5

[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkFBQA0)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkFBQA0)
[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkGnQAK)
[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkGnQAK)
[![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)

`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkFBQA0`
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkGnQAK`

`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkFBQA0`
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkGnQAK`

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @group Configuration
* @description Class used to cache query results returned by the selector classes
*/
@SuppressWarnings('PMD.ExcessivePublicCount')
@SuppressWarnings('PMD.CognitiveComplexity, PMD.ExcessivePublicCount')
public without sharing class LoggerCache {
private static final String DEFAULT_PARTITION_NAME = 'LoggerCache';
private static final Boolean PLATFORM_CACHE_IS_IMMUTABLE = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @description Provides a centralized way to load parameters for SObject handlers & plugins,
* and casts the parameters to common data types
*/
@SuppressWarnings('PMD.CyclomaticComplexity, PMD.ExcessivePublicCount, PMD.PropertyNamingConventions')
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount, PMD.PropertyNamingConventions')
public class LoggerParameter {
// During tests, always load this CMDT record
// so that tests use the same format when calling System.debug()
Expand Down Expand Up @@ -369,6 +369,21 @@ public class LoggerParameter {
private set;
}

/**
* @description Indicates if Nebula Logger will store the transaction heap limits on `LogEntry__c`, retrieved from the class `System.Limits`.
* Controlled by the custom metadata record `LoggerParameter.StoreApexHeapSizeLimit`, or `true` as the default.
* Relies on `LoggerParameter.StoreTransactionLimits` to be true, as well.
*/
public static final Boolean STORE_HEAP_SIZE_LIMIT {
get {
if (STORE_HEAP_SIZE_LIMIT == null) {
STORE_HEAP_SIZE_LIMIT = getBoolean('StoreHeapSizeLimit', true);
}
return STORE_HEAP_SIZE_LIMIT;
}
private set;
}

/**
* @description Indicates if Nebula Logger will store the header values when logging an instance of `System.HttpResponse`.
* Controlled by the custom metadata record `LoggerParameter.StoreHttpResponseHeaderValues`, or `true` as the default.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
>
<label>Store Heap Size Limit</label>
<protected>false</protected>
<values>
<field>Comments__c</field>
<value xsi:nil="true" />
</values>
<values>
<field>Description__c</field>
<value
xsi:type="xsd:string"
>When set to &apos;true&apos; (default), transaction heap limits are retrieved from the class System.Limits and stored on LogEntry__c.

When set to &apos;false&apos;, transaction heap limits are not retrieved or stored. This drastically helps reduce CPU time usage per log entry. The &apos;Store Transaction Limits&apos; Logger Parameter record must also be set to &apos;true&apos; for any limit tracking to occur.</value>
</values>
<values>
<field>Value__c</field>
<value xsi:type="xsd:string">true</value>
</values>
</CustomMetadata>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @description Batch class used to delete old logs, based on `Log__c.LogRetentionDate__c <= :System.today()`
* @see LogBatchPurgeScheduler
*/
@SuppressWarnings('PMD.AvoidGlobalModifier, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList')
@SuppressWarnings('PMD.AvoidGlobalModifier, PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList')
global with sharing class LogBatchPurger implements Database.Batchable<SObject>, Database.Stateful {
private static final Integer DEFAULT_BATCH_SIZE = 2000;
private static final Date LOG_RETENTION_END_DATE = System.today();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @group Log Management
* @description Builds and sends email notifications when internal exceptions occur within the logging system
*/
@SuppressWarnings('PMD.PropertyNamingConventions')
@SuppressWarnings('PMD.CognitiveComplexity, PMD.PropertyNamingConventions')
public without sharing class LoggerEmailSender {
@TestVisible
private static final List<ApexEmailNotification> MOCK_NOTIFICATIONS = new List<ApexEmailNotification>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @group Log Management
* @description Controller class for lwc `loggerSettings`, used to manage records in `LoggerSettings__c`
*/
@SuppressWarnings('PMD.CyclomaticComplexity, PMD.ExcessivePublicCount')
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount')
public without sharing class LoggerSettingsController {
@TestVisible
private static final String CUSTOM_SAVE_METHOD_PREFIX = 'CustomSaveMethod';
Expand Down
20 changes: 6 additions & 14 deletions nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public inherited sharing class ComponentLogger {
logEntryEventBuilder.setRecord(componentLogEntry.record).getLogEntryEvent();
}

logEntryEvent.Timestamp__c = componentLogEntry.timestamp;
logEntryEventBuilder.setTimestamp(componentLogEntry.timestamp);
setBrowserDetails(logEntryEvent, componentLogEntry);
setComponentErrorDetails(logEntryEvent, componentLogEntry.error);
setOriginStackTraceDetails(logEntryEvent, componentLogEntry.stack);
Expand Down Expand Up @@ -106,7 +106,10 @@ public inherited sharing class ComponentLogger {
logEntryEvent.ExceptionSourceActionName__c = exceptionStackTrace.Source?.ActionName;
logEntryEvent.ExceptionSourceApiName__c = exceptionStackTrace.Source?.ApiName;
logEntryEvent.ExceptionSourceMetadataType__c = exceptionStackTrace.Source?.MetadataType.name();
logEntryEvent.ExceptionStackTrace__c = exceptionStackTrace.ParsedStackTraceString;
logEntryEvent.ExceptionStackTrace__c = LoggerDataStore.truncateFieldValue(
Schema.LogEntryEvent__e.ExceptionStackTrace__c,
exceptionStackTrace.ParsedStackTraceString
);
}

@SuppressWarnings('PMD.AvoidDeeplyNestedIfStmts, PMD.CyclomaticComplexity, PMD.CognitiveComplexity, PMD.NcssMethodCount, PMD.StdCyclomaticComplexity')
Expand All @@ -127,25 +130,14 @@ public inherited sharing class ComponentLogger {

// New Origin fields
logEntryEvent.OriginLocation__c = originStackTrace.Location;
logEntryEvent.StackTrace__c = stackTraceString;
logEntryEvent.StackTrace__c = LoggerDataStore.truncateFieldValue(Schema.LogEntryEvent__e.StackTrace__c, stackTraceString);
if (originStackTrace.Source != null) {
logEntryEvent.OriginSourceActionName__c = originStackTrace.Source.ActionName;
logEntryEvent.OriginSourceMetadataType__c = originStackTrace.Source.MetadataType.name();
logEntryEvent.OriginSourceApiName__c = originStackTrace.Source.ApiName;
}
}

private static String truncateFieldValue(Schema.SObjectField field, String value) {
Integer fieldMaxLength = field.getDescribe().getLength();
if (String.isBlank(value)) {
return value;
} else if (value.length() <= fieldMaxLength) {
return value;
}

return value.left(fieldMaxLength);
}

/**
* @description A DTO object used for passing `LoggerSettings__c` details to lightning components
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public inherited sharing class FlowLogger {
this.logEntryEvent.OriginSourceApiName__c = this.flowName;
this.logEntryEvent.OriginSourceMetadataType__c = FLOW_SOURCE_METADATA_TYPE;
this.logEntryEvent.OriginType__c = FLOW_SOURCE_METADATA_TYPE;
this.logEntryEvent.Timestamp__c = this.timestamp;
this.logEntryEventBuilder.setTimestamp(this.timestamp);
// Flow does not have an equivalent to a stack trace, so always clear the fields
this.logEntryEvent.ExceptionStackTrace__c = null;
this.logEntryEvent.StackTrace__c = null;
Expand Down
Loading

0 comments on commit 6453b17

Please sign in to comment.