Skip to content

Commit 6453b17

Browse files
authored
Logger Performance Improvements (#660)
* 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()
1 parent b3a08f0 commit 6453b17

28 files changed

Lines changed: 512 additions & 620 deletions

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

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

8-
## Unlocked Package - v4.13.4
8+
## Unlocked Package - v4.13.5
99

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

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

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

1818
---
1919

nebula-logger/core/main/configuration/classes/LoggerCache.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @group Configuration
88
* @description Class used to cache query results returned by the selector classes
99
*/
10-
@SuppressWarnings('PMD.ExcessivePublicCount')
10+
@SuppressWarnings('PMD.CognitiveComplexity, PMD.ExcessivePublicCount')
1111
public without sharing class LoggerCache {
1212
private static final String DEFAULT_PARTITION_NAME = 'LoggerCache';
1313
private static final Boolean PLATFORM_CACHE_IS_IMMUTABLE = false;

nebula-logger/core/main/configuration/classes/LoggerParameter.cls

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* @description Provides a centralized way to load parameters for SObject handlers & plugins,
99
* and casts the parameters to common data types
1010
*/
11-
@SuppressWarnings('PMD.CyclomaticComplexity, PMD.ExcessivePublicCount, PMD.PropertyNamingConventions')
11+
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount, PMD.PropertyNamingConventions')
1212
public class LoggerParameter {
1313
// During tests, always load this CMDT record
1414
// so that tests use the same format when calling System.debug()
@@ -369,6 +369,21 @@ public class LoggerParameter {
369369
private set;
370370
}
371371

372+
/**
373+
* @description Indicates if Nebula Logger will store the transaction heap limits on `LogEntry__c`, retrieved from the class `System.Limits`.
374+
* Controlled by the custom metadata record `LoggerParameter.StoreApexHeapSizeLimit`, or `true` as the default.
375+
* Relies on `LoggerParameter.StoreTransactionLimits` to be true, as well.
376+
*/
377+
public static final Boolean STORE_HEAP_SIZE_LIMIT {
378+
get {
379+
if (STORE_HEAP_SIZE_LIMIT == null) {
380+
STORE_HEAP_SIZE_LIMIT = getBoolean('StoreHeapSizeLimit', true);
381+
}
382+
return STORE_HEAP_SIZE_LIMIT;
383+
}
384+
private set;
385+
}
386+
372387
/**
373388
* @description Indicates if Nebula Logger will store the header values when logging an instance of `System.HttpResponse`.
374389
* Controlled by the custom metadata record `LoggerParameter.StoreHttpResponseHeaderValues`, or `true` as the default.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<CustomMetadata
3+
xmlns="http://soap.sforce.com/2006/04/metadata"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
6+
>
7+
<label>Store Heap Size Limit</label>
8+
<protected>false</protected>
9+
<values>
10+
<field>Comments__c</field>
11+
<value xsi:nil="true" />
12+
</values>
13+
<values>
14+
<field>Description__c</field>
15+
<value
16+
xsi:type="xsd:string"
17+
>When set to &apos;true&apos; (default), transaction heap limits are retrieved from the class System.Limits and stored on LogEntry__c.
18+
19+
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>
20+
</values>
21+
<values>
22+
<field>Value__c</field>
23+
<value xsi:type="xsd:string">true</value>
24+
</values>
25+
</CustomMetadata>

nebula-logger/core/main/log-management/classes/LogBatchPurger.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* @description Batch class used to delete old logs, based on `Log__c.LogRetentionDate__c <= :System.today()`
99
* @see LogBatchPurgeScheduler
1010
*/
11-
@SuppressWarnings('PMD.AvoidGlobalModifier, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList')
11+
@SuppressWarnings('PMD.AvoidGlobalModifier, PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList')
1212
global with sharing class LogBatchPurger implements Database.Batchable<SObject>, Database.Stateful {
1313
private static final Integer DEFAULT_BATCH_SIZE = 2000;
1414
private static final Date LOG_RETENTION_END_DATE = System.today();

nebula-logger/core/main/log-management/classes/LoggerEmailSender.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @group Log Management
88
* @description Builds and sends email notifications when internal exceptions occur within the logging system
99
*/
10-
@SuppressWarnings('PMD.PropertyNamingConventions')
10+
@SuppressWarnings('PMD.CognitiveComplexity, PMD.PropertyNamingConventions')
1111
public without sharing class LoggerEmailSender {
1212
@TestVisible
1313
private static final List<ApexEmailNotification> MOCK_NOTIFICATIONS = new List<ApexEmailNotification>();

nebula-logger/core/main/log-management/classes/LoggerSettingsController.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* @group Log Management
88
* @description Controller class for lwc `loggerSettings`, used to manage records in `LoggerSettings__c`
99
*/
10-
@SuppressWarnings('PMD.CyclomaticComplexity, PMD.ExcessivePublicCount')
10+
@SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount')
1111
public without sharing class LoggerSettingsController {
1212
@TestVisible
1313
private static final String CUSTOM_SAVE_METHOD_PREFIX = 'CustomSaveMethod';

nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public inherited sharing class ComponentLogger {
6161
logEntryEventBuilder.setRecord(componentLogEntry.record).getLogEntryEvent();
6262
}
6363

64-
logEntryEvent.Timestamp__c = componentLogEntry.timestamp;
64+
logEntryEventBuilder.setTimestamp(componentLogEntry.timestamp);
6565
setBrowserDetails(logEntryEvent, componentLogEntry);
6666
setComponentErrorDetails(logEntryEvent, componentLogEntry.error);
6767
setOriginStackTraceDetails(logEntryEvent, componentLogEntry.stack);
@@ -106,7 +106,10 @@ public inherited sharing class ComponentLogger {
106106
logEntryEvent.ExceptionSourceActionName__c = exceptionStackTrace.Source?.ActionName;
107107
logEntryEvent.ExceptionSourceApiName__c = exceptionStackTrace.Source?.ApiName;
108108
logEntryEvent.ExceptionSourceMetadataType__c = exceptionStackTrace.Source?.MetadataType.name();
109-
logEntryEvent.ExceptionStackTrace__c = exceptionStackTrace.ParsedStackTraceString;
109+
logEntryEvent.ExceptionStackTrace__c = LoggerDataStore.truncateFieldValue(
110+
Schema.LogEntryEvent__e.ExceptionStackTrace__c,
111+
exceptionStackTrace.ParsedStackTraceString
112+
);
110113
}
111114

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

128131
// New Origin fields
129132
logEntryEvent.OriginLocation__c = originStackTrace.Location;
130-
logEntryEvent.StackTrace__c = stackTraceString;
133+
logEntryEvent.StackTrace__c = LoggerDataStore.truncateFieldValue(Schema.LogEntryEvent__e.StackTrace__c, stackTraceString);
131134
if (originStackTrace.Source != null) {
132135
logEntryEvent.OriginSourceActionName__c = originStackTrace.Source.ActionName;
133136
logEntryEvent.OriginSourceMetadataType__c = originStackTrace.Source.MetadataType.name();
134137
logEntryEvent.OriginSourceApiName__c = originStackTrace.Source.ApiName;
135138
}
136139
}
137140

138-
private static String truncateFieldValue(Schema.SObjectField field, String value) {
139-
Integer fieldMaxLength = field.getDescribe().getLength();
140-
if (String.isBlank(value)) {
141-
return value;
142-
} else if (value.length() <= fieldMaxLength) {
143-
return value;
144-
}
145-
146-
return value.left(fieldMaxLength);
147-
}
148-
149141
/**
150142
* @description A DTO object used for passing `LoggerSettings__c` details to lightning components
151143
*/

nebula-logger/core/main/logger-engine/classes/FlowLogger.cls

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ public inherited sharing class FlowLogger {
132132
this.logEntryEvent.OriginSourceApiName__c = this.flowName;
133133
this.logEntryEvent.OriginSourceMetadataType__c = FLOW_SOURCE_METADATA_TYPE;
134134
this.logEntryEvent.OriginType__c = FLOW_SOURCE_METADATA_TYPE;
135-
this.logEntryEvent.Timestamp__c = this.timestamp;
135+
this.logEntryEventBuilder.setTimestamp(this.timestamp);
136136
// Flow does not have an equivalent to a stack trace, so always clear the fields
137137
this.logEntryEvent.ExceptionStackTrace__c = null;
138138
this.logEntryEvent.StackTrace__c = null;

0 commit comments

Comments
 (0)