Skip to content

Commit 91f2eb2

Browse files
authored
Bugfixes for Scenario Rules (#722)
* Fixed #538 by changing LogEntryEventHandler to always save log entry events when LoggerSettings__c.DefaultPlatformEventStorageLoggingLevel__c is null. When previously using the user's logging level from LoggerSettings__c as a fallback value, it could result in entries being lost if a matching LoggerScenarioRule__mdt exists with a lower logging level * Fixed some unreported issues in Logger with setScenario() and endScenario() not properly updating the field values returned by getUserSettings(). Now, both setScenario() and endScenario() wipe out & reload the in-memory instance of LoggerSettings__c (before applying any matching LoggerScenarioRule__mdt records) to ensure that there are not any remnants lingering when multiple LoggerScenarioRule__mdt records have been applied in a single transaction
1 parent 1f7dbf8 commit 91f2eb2

File tree

8 files changed

+177
-23
lines changed

8 files changed

+177
-23
lines changed

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.15
8+
## Unlocked Package - v4.13.16
99

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

14-
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oF5QAI`
14+
`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015oFjQAI`
1515

16-
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oF5QAI`
16+
`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015oFjQAI`
1717

1818
---
1919

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

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,18 @@ public without sharing class LogEntryEventHandler extends LoggerSObjectHandler {
7878
platformEventStorageLocation = scenarioRule.PlatformEventStorageLocation__c;
7979
}
8080
}
81-
// Filter first based on storage logging location
82-
if (platformEventStorageLocation == DEFAULT_STORAGE_LOCATION_NAME) {
83-
// Then filter further based on storage logging level
84-
String userStorageLoggingLevelName = loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c;
85-
if (String.isBlank(userStorageLoggingLevelName)) {
86-
userStorageLoggingLevelName = loggingUserSettings.LoggingLevel__c;
87-
}
88-
System.LoggingLevel userStorageLoggingLevel = System.LoggingLevel.valueOf(userStorageLoggingLevelName);
81+
82+
if (platformEventStorageLocation != DEFAULT_STORAGE_LOCATION_NAME) {
83+
// For any other storage location (besides CUSTOM_OBJECTS), it's assumed that a plugin will be handling everything,
84+
// so there's nothing else to do here
85+
continue;
86+
}
87+
88+
if (String.isBlank(loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c)) {
89+
// DefaultPlatformEventStorageLoggingLevel__c is optional - if it's null, then always save the event
90+
logEntryEventsToSave.add(logEntryEvent);
91+
} else {
92+
System.LoggingLevel userStorageLoggingLevel = System.LoggingLevel.valueOf(loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c);
8993
System.LoggingLevel entryLoggingLevel = System.LoggingLevel.valueOf(logEntryEvent.LoggingLevel__c);
9094
if (userStorageLoggingLevel.ordinal() <= entryLoggingLevel.ordinal()) {
9195
logEntryEventsToSave.add(logEntryEvent);

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
global with sharing class Logger {
1616
// There's no reliable way to get the version number dynamically in Apex
1717
@TestVisible
18-
private static final String CURRENT_VERSION_NUMBER = 'v4.13.15';
18+
private static final String CURRENT_VERSION_NUMBER = 'v4.13.16';
1919
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
2020
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
2121
private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.';
@@ -27,6 +27,7 @@ global with sharing class Logger {
2727

2828
private static AsyncContext currentAsyncContext;
2929
private static String currentEntryScenario;
30+
private static LoggerSettings__c currentUserSettings;
3031
@TestVisible
3132
private static String lastSaveMethodNameUsed;
3233
private static List<String> orderedScenarios = new List<String>();
@@ -35,7 +36,6 @@ global with sharing class Logger {
3536
private static Integer saveLogCallCount = 0;
3637
private static Boolean suspendSaving = false;
3738
private static String transactionScenario;
38-
private static LoggerSettings__c userSettings;
3939

4040
private static final List<String> CLASSES_TO_IGNORE {
4141
get {
@@ -347,11 +347,11 @@ global with sharing class Logger {
347347
*/
348348
public static LoggerSettings__c getUserSettings() {
349349
// Only load the current user's settings once - this allows the instance to be modified in memory (as well as upserted if any changes should be persisted)
350-
if (userSettings == null) {
350+
if (currentUserSettings == null) {
351351
Schema.User currentUser = new Schema.User(Id = System.UserInfo.getUserId(), ProfileId = System.UserInfo.getProfileId());
352-
userSettings = getUserSettings(currentUser);
352+
currentUserSettings = getUserSettings(currentUser);
353353
}
354-
return userSettings;
354+
return currentUserSettings;
355355
}
356356

357357
/**
@@ -3088,6 +3088,8 @@ global with sharing class Logger {
30883088
}
30893089
LoggerScenarioRule__mdt matchingScenarioRule = LoggerScenarioRule.getInstance(scenario);
30903090
if (matchingScenarioRule != null) {
3091+
// Clear & reload the user's settings so that there aren't any lingering changes made by previous scenario rules
3092+
currentUserSettings = null;
30913093
LoggerSettings__c userSettings = getUserSettings();
30923094
if (String.isNotBlank(matchingScenarioRule.IsLoggerEnabled__c)) {
30933095
userSettings.IsEnabled__c = Boolean.valueOf(matchingScenarioRule.IsLoggerEnabled__c);
@@ -3138,6 +3140,9 @@ global with sharing class Logger {
31383140
}
31393141
if (String.isBlank(previousScenario)) {
31403142
currentEntryScenario = null;
3143+
// Clear & reload the user's settings so that there aren't any lingering changes made by previous scenario rules
3144+
// The settings will then be auto-reloaded when setScenario(previousScenario) is called below
3145+
currentUserSettings = null;
31413146
}
31423147

31433148
setScenario(previousScenario);

nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//------------------------------------------------------------------------------------------------//
55
import FORM_FACTOR from '@salesforce/client/formFactor';
66

7-
const CURRENT_VERSION_NUMBER = 'v4.13.15';
7+
const CURRENT_VERSION_NUMBER = 'v4.13.16';
88

99
// JavaScript equivalent to the Apex class ComponentLogger.ComponentLogEntry
1010
const ComponentLogEntry = class {

nebula-logger/core/tests/log-management/classes/LogEntryEventHandler_Tests.cls

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
@SuppressWarnings('PMD.ApexDoc, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList, PMD.MethodNamingConventions, PMD.NcssMethodCount, PMD.NcssTypeCount')
77
@IsTest(IsParallel=false)
88
private class LogEntryEventHandler_Tests {
9+
private static final String MOCK_TRANSACTION_ID = System.UUID.randomUUID().toString();
10+
11+
private static Integer currentLogEntryNumber = 1;
12+
913
@IsTest
1014
static void it_should_return_the_logEntryEvent_sobjectType() {
1115
System.Assert.areEqual(Schema.LogEntryEvent__e.SObjectType, new LogEntryEventHandler().getSObjectType());
@@ -99,6 +103,60 @@ private class LogEntryEventHandler_Tests {
99103
System.Assert.areEqual(matchingLogEntryEvent.Message__c, logEntries.get(0).Message__c);
100104
}
101105

106+
@IsTest
107+
static void it_should_create_log_entry_data_with_matching_scenario_rule_when_platform_event_storage_logging_level_is_null() {
108+
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
109+
LoggerTestConfigurator.setupMockSObjectHandlerConfigurations();
110+
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.Log__c.SObjectType).IsEnabled__c = false;
111+
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.LogEntry__c.SObjectType).IsEnabled__c = false;
112+
System.Assert.areEqual(0, [SELECT COUNT() FROM Log__c]);
113+
System.Assert.areEqual(0, [SELECT COUNT() FROM LogEntry__c]);
114+
LoggerSettings__c settings = Logger.getUserSettings();
115+
settings.LoggingLevel__c = System.LoggingLevel.WARN.name();
116+
settings.DefaultPlatformEventStorageLocation__c = 'CUSTOM_OBJECTS';
117+
settings.DefaultPlatformEventStorageLoggingLevel__c = null;
118+
upsert settings;
119+
String someScenario = 'some scenario with a configured rule';
120+
LoggerScenarioRule.setMock(new LoggerScenarioRule__mdt(IsEnabled__c = true, Scenario__c = someScenario));
121+
LogEntryEvent__e firstMatchingLogEntryEvent = createLogEntryEvent();
122+
firstMatchingLogEntryEvent.LoggingLevel__c = System.LoggingLevel.ERROR.name();
123+
firstMatchingLogEntryEvent.Message__c = 'This event should be saved in LogEntry__c';
124+
firstMatchingLogEntryEvent.TransactionScenario__c = someScenario;
125+
LogEntryEvent__e secondMatchingLogEntryEvent = createLogEntryEvent();
126+
secondMatchingLogEntryEvent.LoggingLevel__c = System.LoggingLevel.FINEST.name();
127+
secondMatchingLogEntryEvent.Message__c = 'This event should ALSO be saved in LogEntry__c';
128+
secondMatchingLogEntryEvent.TransactionScenario__c = someScenario;
129+
List<LogEntryEvent__e> logEntryEvents = new List<LogEntryEvent__e>{ firstMatchingLogEntryEvent, secondMatchingLogEntryEvent };
130+
131+
List<Database.SaveResult> saveResults = LoggerMockDataStore.getEventBus().publishRecords(logEntryEvents);
132+
LoggerMockDataStore.getEventBus().deliver(new LogEntryEventHandler());
133+
134+
System.Assert.areEqual(logEntryEvents.size(), saveResults.size());
135+
for (Database.SaveResult saveResult : saveResults) {
136+
System.Assert.isTrue(saveResult.isSuccess(), saveResult.getErrors().toString());
137+
}
138+
System.Assert.areEqual(
139+
1,
140+
LoggerSObjectHandler.getExecutedHandlers().get(Schema.LogEntryEvent__e.SObjectType).size(),
141+
'Handler class should have executed one time for AFTER_INSERT'
142+
);
143+
System.Assert.areEqual(1, [SELECT COUNT() FROM Log__c]);
144+
LogEntry__c firstMatchingLogEntry = [
145+
SELECT Id, LoggingLevel__c, Message__c
146+
FROM LogEntry__c
147+
WHERE LoggingLevel__c = :firstMatchingLogEntryEvent.LoggingLevel__c
148+
];
149+
System.Assert.areEqual(firstMatchingLogEntryEvent.LoggingLevel__c, firstMatchingLogEntry.LoggingLevel__c);
150+
System.Assert.areEqual(firstMatchingLogEntryEvent.Message__c, firstMatchingLogEntry.Message__c);
151+
LogEntry__c secondMatchingLogEntry = [
152+
SELECT Id, LoggingLevel__c, Message__c
153+
FROM LogEntry__c
154+
WHERE LoggingLevel__c = :secondMatchingLogEntryEvent.LoggingLevel__c
155+
];
156+
System.Assert.areEqual(secondMatchingLogEntryEvent.LoggingLevel__c, secondMatchingLogEntry.LoggingLevel__c);
157+
System.Assert.areEqual(secondMatchingLogEntryEvent.Message__c, secondMatchingLogEntry.Message__c);
158+
}
159+
102160
@IsTest
103161
static void it_should_not_create_log_or_log_entry_data_when_platform_event_storage_location_is_null_in_logger_settings() {
104162
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
@@ -1222,6 +1280,8 @@ private class LogEntryEventHandler_Tests {
12221280
logEntryEvent.RecordCollectionType__c = 'Single';
12231281
logEntryEvent.RecordId__c = System.UserInfo.getUserId();
12241282
logEntryEvent.TimestampString__c = String.valueOf(logEntryEvent.Timestamp__c.getTime());
1283+
logEntryEvent.TransactionEntryNumber__c = currentLogEntryNumber++;
1284+
logEntryEvent.TransactionId__c = MOCK_TRANSACTION_ID;
12251285
logEntryEvent.UserLoggingLevel__c = System.LoggingLevel.INFO.name();
12261286
logEntryEvent.UserLoggingLevelOrdinal__c = System.LoggingLevel.INFO.ordinal();
12271287
logEntryEvent = (LogEntryEvent__e) LoggerMockDataCreator.setReadOnlyField(logEntryEvent, Schema.LogEntryEvent__e.EventUuid, System.UUID.randomUUID());

nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,90 @@ private class Logger_Tests {
476476
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserLoggingLevel().name());
477477
}
478478

479+
@IsTest
480+
static void it_should_revert_to_previous_user_settings_when_the_only_scenario_is_ended() {
481+
LoggerSettings__c userSettingsToCreate = (LoggerSettings__c) Schema.LoggerSettings__c.SObjectType.newSObject(null, true);
482+
userSettingsToCreate.IsRecordFieldStrippingEnabled__c = true;
483+
userSettingsToCreate.LoggingLevel__c = System.LoggingLevel.INFO.name();
484+
userSettingsToCreate.SetupOwnerId = System.UserInfo.getUserId();
485+
insert userSettingsToCreate;
486+
LoggerSettings__c originalUserSettings = Logger.getUserSettings().clone();
487+
System.LoggingLevel originalLoggingLevel = Logger.getUserLoggingLevel();
488+
System.Assert.isNull(Logger.getScenario());
489+
String mockScenario = 'some test scenario ';
490+
LoggerScenarioRule__mdt mockScenarioRule = new LoggerScenarioRule__mdt(
491+
IsEnabled__c = true,
492+
IsRecordFieldStrippingEnabled__c = String.valueOf(false),
493+
Scenario__c = mockScenario,
494+
UserLoggingLevel__c = System.LoggingLevel.FINE.name()
495+
);
496+
LoggerScenarioRule.setMock(mockScenarioRule);
497+
System.Assert.areNotEqual(originalLoggingLevel.name(), mockScenarioRule.UserLoggingLevel__c);
498+
Logger.setScenario(mockScenario);
499+
System.Assert.areEqual(mockScenario, Logger.getScenario());
500+
System.Assert.areEqual(mockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
501+
502+
Logger.endScenario(mockScenario);
503+
504+
System.Assert.areEqual(originalUserSettings.IsEnabled__c, Logger.getUserSettings().IsEnabled__c);
505+
System.Assert.areEqual(originalUserSettings.IsAnonymousModeEnabled__c, Logger.getUserSettings().IsAnonymousModeEnabled__c);
506+
System.Assert.areEqual(originalUserSettings.IsApexSystemDebugLoggingEnabled__c, Logger.getUserSettings().IsApexSystemDebugLoggingEnabled__c);
507+
System.Assert.areEqual(originalUserSettings.IsDataMaskingEnabled__c, Logger.getUserSettings().IsDataMaskingEnabled__c);
508+
System.Assert.areEqual(originalUserSettings.IsJavaScriptConsoleLoggingEnabled__c, Logger.getUserSettings().IsJavaScriptConsoleLoggingEnabled__c);
509+
System.Assert.areEqual(originalUserSettings.IsRecordFieldStrippingEnabled__c, Logger.getUserSettings().IsRecordFieldStrippingEnabled__c);
510+
System.Assert.areEqual(originalUserSettings.IsSavingEnabled__c, Logger.getUserSettings().IsSavingEnabled__c);
511+
System.Assert.areEqual(originalUserSettings.LoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
512+
}
513+
514+
@IsTest
515+
static void it_should_revert_to_previous_user_settings_with_scenario_rule_applied_when_scenario_is_ended_and_other_scenarios_have_been_used() {
516+
LoggerSettings__c userSettingsToCreate = (LoggerSettings__c) Schema.LoggerSettings__c.SObjectType.newSObject(null, true);
517+
userSettingsToCreate.IsRecordFieldStrippingEnabled__c = true;
518+
userSettingsToCreate.LoggingLevel__c = System.LoggingLevel.INFO.name();
519+
userSettingsToCreate.SetupOwnerId = System.UserInfo.getUserId();
520+
insert userSettingsToCreate;
521+
LoggerSettings__c originalUserSettings = Logger.getUserSettings().clone();
522+
System.Assert.isNull(Logger.getScenario());
523+
String firstMockScenario = 'some test scenario ';
524+
LoggerScenarioRule__mdt firstMockScenarioRule = new LoggerScenarioRule__mdt(
525+
IsEnabled__c = true,
526+
IsRecordFieldStrippingEnabled__c = String.valueOf(false),
527+
Scenario__c = firstMockScenario,
528+
UserLoggingLevel__c = System.LoggingLevel.FINE.name()
529+
);
530+
LoggerScenarioRule.setMock(firstMockScenarioRule);
531+
Logger.setScenario(firstMockScenario);
532+
System.Assert.areEqual(firstMockScenario, Logger.getScenario());
533+
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
534+
String secondMockScenario = 'another test scenario ';
535+
LoggerScenarioRule__mdt secondMockScenarioRule = new LoggerScenarioRule__mdt(
536+
IsEnabled__c = true,
537+
Scenario__c = secondMockScenario,
538+
UserLoggingLevel__c = System.LoggingLevel.DEBUG.name()
539+
);
540+
LoggerScenarioRule.setMock(secondMockScenarioRule);
541+
System.Assert.areNotEqual(firstMockScenarioRule.UserLoggingLevel__c, secondMockScenarioRule.UserLoggingLevel__c);
542+
Logger.setScenario(secondMockScenario);
543+
System.Assert.areEqual(secondMockScenario, Logger.getScenario());
544+
System.Assert.areEqual(originalUserSettings.IsRecordFieldStrippingEnabled__c, Logger.getUserSettings().IsRecordFieldStrippingEnabled__c);
545+
System.Assert.areEqual(secondMockScenarioRule.UserLoggingLevel__c, Logger.getUserLoggingLevel().name());
546+
547+
Logger.endScenario(secondMockScenario);
548+
549+
System.Assert.areEqual(firstMockScenario, Logger.getScenario());
550+
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
551+
System.Assert.areEqual(
552+
Boolean.valueOf(firstMockScenarioRule.IsRecordFieldStrippingEnabled__c),
553+
Logger.getUserSettings().IsRecordFieldStrippingEnabled__c
554+
);
555+
System.Assert.areEqual(originalUserSettings.IsEnabled__c, Logger.getUserSettings().IsEnabled__c);
556+
System.Assert.areEqual(originalUserSettings.IsAnonymousModeEnabled__c, Logger.getUserSettings().IsAnonymousModeEnabled__c);
557+
System.Assert.areEqual(originalUserSettings.IsApexSystemDebugLoggingEnabled__c, Logger.getUserSettings().IsApexSystemDebugLoggingEnabled__c);
558+
System.Assert.areEqual(originalUserSettings.IsDataMaskingEnabled__c, Logger.getUserSettings().IsDataMaskingEnabled__c);
559+
System.Assert.areEqual(originalUserSettings.IsJavaScriptConsoleLoggingEnabled__c, Logger.getUserSettings().IsJavaScriptConsoleLoggingEnabled__c);
560+
System.Assert.areEqual(originalUserSettings.IsSavingEnabled__c, Logger.getUserSettings().IsSavingEnabled__c);
561+
}
562+
479563
@IsTest
480564
static void it_should_not_ignore_origin_when_apex_class_type_not_specified() {
481565
// Don't bother testing stack trace logic when using a namespace prefix - there are

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nebula-logger",
3-
"version": "4.13.15",
3+
"version": "4.13.16",
44
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
55
"author": "Jonathan Gillespie",
66
"license": "MIT",

0 commit comments

Comments
 (0)