Skip to content

Commit

Permalink
Bugfixes for Scenario Rules (#722)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jongpie authored Jul 31, 2024
1 parent 1f7dbf8 commit 91f2eb2
Show file tree
Hide file tree
Showing 8 changed files with 177 additions and 23 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.15
## Unlocked Package - v4.13.16

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

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

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

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,18 @@ public without sharing class LogEntryEventHandler extends LoggerSObjectHandler {
platformEventStorageLocation = scenarioRule.PlatformEventStorageLocation__c;
}
}
// Filter first based on storage logging location
if (platformEventStorageLocation == DEFAULT_STORAGE_LOCATION_NAME) {
// Then filter further based on storage logging level
String userStorageLoggingLevelName = loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c;
if (String.isBlank(userStorageLoggingLevelName)) {
userStorageLoggingLevelName = loggingUserSettings.LoggingLevel__c;
}
System.LoggingLevel userStorageLoggingLevel = System.LoggingLevel.valueOf(userStorageLoggingLevelName);

if (platformEventStorageLocation != DEFAULT_STORAGE_LOCATION_NAME) {
// For any other storage location (besides CUSTOM_OBJECTS), it's assumed that a plugin will be handling everything,
// so there's nothing else to do here
continue;
}

if (String.isBlank(loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c)) {
// DefaultPlatformEventStorageLoggingLevel__c is optional - if it's null, then always save the event
logEntryEventsToSave.add(logEntryEvent);
} else {
System.LoggingLevel userStorageLoggingLevel = System.LoggingLevel.valueOf(loggingUserSettings.DefaultPlatformEventStorageLoggingLevel__c);
System.LoggingLevel entryLoggingLevel = System.LoggingLevel.valueOf(logEntryEvent.LoggingLevel__c);
if (userStorageLoggingLevel.ordinal() <= entryLoggingLevel.ordinal()) {
logEntryEventsToSave.add(logEntryEvent);
Expand Down
15 changes: 10 additions & 5 deletions nebula-logger/core/main/logger-engine/classes/Logger.cls
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
global with sharing class Logger {
// There's no reliable way to get the version number dynamically in Apex
@TestVisible
private static final String CURRENT_VERSION_NUMBER = 'v4.13.15';
private static final String CURRENT_VERSION_NUMBER = 'v4.13.16';
private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG;
private static final List<LogEntryEventBuilder> LOG_ENTRIES_BUFFER = new List<LogEntryEventBuilder>();
private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.';
Expand All @@ -27,6 +27,7 @@ global with sharing class Logger {

private static AsyncContext currentAsyncContext;
private static String currentEntryScenario;
private static LoggerSettings__c currentUserSettings;
@TestVisible
private static String lastSaveMethodNameUsed;
private static List<String> orderedScenarios = new List<String>();
Expand All @@ -35,7 +36,6 @@ global with sharing class Logger {
private static Integer saveLogCallCount = 0;
private static Boolean suspendSaving = false;
private static String transactionScenario;
private static LoggerSettings__c userSettings;

private static final List<String> CLASSES_TO_IGNORE {
get {
Expand Down Expand Up @@ -347,11 +347,11 @@ global with sharing class Logger {
*/
public static LoggerSettings__c getUserSettings() {
// 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)
if (userSettings == null) {
if (currentUserSettings == null) {
Schema.User currentUser = new Schema.User(Id = System.UserInfo.getUserId(), ProfileId = System.UserInfo.getProfileId());
userSettings = getUserSettings(currentUser);
currentUserSettings = getUserSettings(currentUser);
}
return userSettings;
return currentUserSettings;
}

/**
Expand Down Expand Up @@ -3088,6 +3088,8 @@ global with sharing class Logger {
}
LoggerScenarioRule__mdt matchingScenarioRule = LoggerScenarioRule.getInstance(scenario);
if (matchingScenarioRule != null) {
// Clear & reload the user's settings so that there aren't any lingering changes made by previous scenario rules
currentUserSettings = null;
LoggerSettings__c userSettings = getUserSettings();
if (String.isNotBlank(matchingScenarioRule.IsLoggerEnabled__c)) {
userSettings.IsEnabled__c = Boolean.valueOf(matchingScenarioRule.IsLoggerEnabled__c);
Expand Down Expand Up @@ -3138,6 +3140,9 @@ global with sharing class Logger {
}
if (String.isBlank(previousScenario)) {
currentEntryScenario = null;
// Clear & reload the user's settings so that there aren't any lingering changes made by previous scenario rules
// The settings will then be auto-reloaded when setScenario(previousScenario) is called below
currentUserSettings = null;
}

setScenario(previousScenario);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
//------------------------------------------------------------------------------------------------//
import FORM_FACTOR from '@salesforce/client/formFactor';

const CURRENT_VERSION_NUMBER = 'v4.13.15';
const CURRENT_VERSION_NUMBER = 'v4.13.16';

// JavaScript equivalent to the Apex class ComponentLogger.ComponentLogEntry
const ComponentLogEntry = class {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
@SuppressWarnings('PMD.ApexDoc, PMD.CyclomaticComplexity, PMD.ExcessiveParameterList, PMD.MethodNamingConventions, PMD.NcssMethodCount, PMD.NcssTypeCount')
@IsTest(IsParallel=false)
private class LogEntryEventHandler_Tests {
private static final String MOCK_TRANSACTION_ID = System.UUID.randomUUID().toString();

private static Integer currentLogEntryNumber = 1;

@IsTest
static void it_should_return_the_logEntryEvent_sobjectType() {
System.Assert.areEqual(Schema.LogEntryEvent__e.SObjectType, new LogEntryEventHandler().getSObjectType());
Expand Down Expand Up @@ -99,6 +103,60 @@ private class LogEntryEventHandler_Tests {
System.Assert.areEqual(matchingLogEntryEvent.Message__c, logEntries.get(0).Message__c);
}

@IsTest
static void it_should_create_log_entry_data_with_matching_scenario_rule_when_platform_event_storage_logging_level_is_null() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
LoggerTestConfigurator.setupMockSObjectHandlerConfigurations();
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.Log__c.SObjectType).IsEnabled__c = false;
LoggerTestConfigurator.getSObjectHandlerConfiguration(Schema.LogEntry__c.SObjectType).IsEnabled__c = false;
System.Assert.areEqual(0, [SELECT COUNT() FROM Log__c]);
System.Assert.areEqual(0, [SELECT COUNT() FROM LogEntry__c]);
LoggerSettings__c settings = Logger.getUserSettings();
settings.LoggingLevel__c = System.LoggingLevel.WARN.name();
settings.DefaultPlatformEventStorageLocation__c = 'CUSTOM_OBJECTS';
settings.DefaultPlatformEventStorageLoggingLevel__c = null;
upsert settings;
String someScenario = 'some scenario with a configured rule';
LoggerScenarioRule.setMock(new LoggerScenarioRule__mdt(IsEnabled__c = true, Scenario__c = someScenario));
LogEntryEvent__e firstMatchingLogEntryEvent = createLogEntryEvent();
firstMatchingLogEntryEvent.LoggingLevel__c = System.LoggingLevel.ERROR.name();
firstMatchingLogEntryEvent.Message__c = 'This event should be saved in LogEntry__c';
firstMatchingLogEntryEvent.TransactionScenario__c = someScenario;
LogEntryEvent__e secondMatchingLogEntryEvent = createLogEntryEvent();
secondMatchingLogEntryEvent.LoggingLevel__c = System.LoggingLevel.FINEST.name();
secondMatchingLogEntryEvent.Message__c = 'This event should ALSO be saved in LogEntry__c';
secondMatchingLogEntryEvent.TransactionScenario__c = someScenario;
List<LogEntryEvent__e> logEntryEvents = new List<LogEntryEvent__e>{ firstMatchingLogEntryEvent, secondMatchingLogEntryEvent };

List<Database.SaveResult> saveResults = LoggerMockDataStore.getEventBus().publishRecords(logEntryEvents);
LoggerMockDataStore.getEventBus().deliver(new LogEntryEventHandler());

System.Assert.areEqual(logEntryEvents.size(), saveResults.size());
for (Database.SaveResult saveResult : saveResults) {
System.Assert.isTrue(saveResult.isSuccess(), saveResult.getErrors().toString());
}
System.Assert.areEqual(
1,
LoggerSObjectHandler.getExecutedHandlers().get(Schema.LogEntryEvent__e.SObjectType).size(),
'Handler class should have executed one time for AFTER_INSERT'
);
System.Assert.areEqual(1, [SELECT COUNT() FROM Log__c]);
LogEntry__c firstMatchingLogEntry = [
SELECT Id, LoggingLevel__c, Message__c
FROM LogEntry__c
WHERE LoggingLevel__c = :firstMatchingLogEntryEvent.LoggingLevel__c
];
System.Assert.areEqual(firstMatchingLogEntryEvent.LoggingLevel__c, firstMatchingLogEntry.LoggingLevel__c);
System.Assert.areEqual(firstMatchingLogEntryEvent.Message__c, firstMatchingLogEntry.Message__c);
LogEntry__c secondMatchingLogEntry = [
SELECT Id, LoggingLevel__c, Message__c
FROM LogEntry__c
WHERE LoggingLevel__c = :secondMatchingLogEntryEvent.LoggingLevel__c
];
System.Assert.areEqual(secondMatchingLogEntryEvent.LoggingLevel__c, secondMatchingLogEntry.LoggingLevel__c);
System.Assert.areEqual(secondMatchingLogEntryEvent.Message__c, secondMatchingLogEntry.Message__c);
}

@IsTest
static void it_should_not_create_log_or_log_entry_data_when_platform_event_storage_location_is_null_in_logger_settings() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
Expand Down Expand Up @@ -1222,6 +1280,8 @@ private class LogEntryEventHandler_Tests {
logEntryEvent.RecordCollectionType__c = 'Single';
logEntryEvent.RecordId__c = System.UserInfo.getUserId();
logEntryEvent.TimestampString__c = String.valueOf(logEntryEvent.Timestamp__c.getTime());
logEntryEvent.TransactionEntryNumber__c = currentLogEntryNumber++;
logEntryEvent.TransactionId__c = MOCK_TRANSACTION_ID;
logEntryEvent.UserLoggingLevel__c = System.LoggingLevel.INFO.name();
logEntryEvent.UserLoggingLevelOrdinal__c = System.LoggingLevel.INFO.ordinal();
logEntryEvent = (LogEntryEvent__e) LoggerMockDataCreator.setReadOnlyField(logEntryEvent, Schema.LogEntryEvent__e.EventUuid, System.UUID.randomUUID());
Expand Down
84 changes: 84 additions & 0 deletions nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,90 @@ private class Logger_Tests {
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserLoggingLevel().name());
}

@IsTest
static void it_should_revert_to_previous_user_settings_when_the_only_scenario_is_ended() {
LoggerSettings__c userSettingsToCreate = (LoggerSettings__c) Schema.LoggerSettings__c.SObjectType.newSObject(null, true);
userSettingsToCreate.IsRecordFieldStrippingEnabled__c = true;
userSettingsToCreate.LoggingLevel__c = System.LoggingLevel.INFO.name();
userSettingsToCreate.SetupOwnerId = System.UserInfo.getUserId();
insert userSettingsToCreate;
LoggerSettings__c originalUserSettings = Logger.getUserSettings().clone();
System.LoggingLevel originalLoggingLevel = Logger.getUserLoggingLevel();
System.Assert.isNull(Logger.getScenario());
String mockScenario = 'some test scenario ';
LoggerScenarioRule__mdt mockScenarioRule = new LoggerScenarioRule__mdt(
IsEnabled__c = true,
IsRecordFieldStrippingEnabled__c = String.valueOf(false),
Scenario__c = mockScenario,
UserLoggingLevel__c = System.LoggingLevel.FINE.name()
);
LoggerScenarioRule.setMock(mockScenarioRule);
System.Assert.areNotEqual(originalLoggingLevel.name(), mockScenarioRule.UserLoggingLevel__c);
Logger.setScenario(mockScenario);
System.Assert.areEqual(mockScenario, Logger.getScenario());
System.Assert.areEqual(mockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);

Logger.endScenario(mockScenario);

System.Assert.areEqual(originalUserSettings.IsEnabled__c, Logger.getUserSettings().IsEnabled__c);
System.Assert.areEqual(originalUserSettings.IsAnonymousModeEnabled__c, Logger.getUserSettings().IsAnonymousModeEnabled__c);
System.Assert.areEqual(originalUserSettings.IsApexSystemDebugLoggingEnabled__c, Logger.getUserSettings().IsApexSystemDebugLoggingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsDataMaskingEnabled__c, Logger.getUserSettings().IsDataMaskingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsJavaScriptConsoleLoggingEnabled__c, Logger.getUserSettings().IsJavaScriptConsoleLoggingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsRecordFieldStrippingEnabled__c, Logger.getUserSettings().IsRecordFieldStrippingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsSavingEnabled__c, Logger.getUserSettings().IsSavingEnabled__c);
System.Assert.areEqual(originalUserSettings.LoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
}

@IsTest
static void it_should_revert_to_previous_user_settings_with_scenario_rule_applied_when_scenario_is_ended_and_other_scenarios_have_been_used() {
LoggerSettings__c userSettingsToCreate = (LoggerSettings__c) Schema.LoggerSettings__c.SObjectType.newSObject(null, true);
userSettingsToCreate.IsRecordFieldStrippingEnabled__c = true;
userSettingsToCreate.LoggingLevel__c = System.LoggingLevel.INFO.name();
userSettingsToCreate.SetupOwnerId = System.UserInfo.getUserId();
insert userSettingsToCreate;
LoggerSettings__c originalUserSettings = Logger.getUserSettings().clone();
System.Assert.isNull(Logger.getScenario());
String firstMockScenario = 'some test scenario ';
LoggerScenarioRule__mdt firstMockScenarioRule = new LoggerScenarioRule__mdt(
IsEnabled__c = true,
IsRecordFieldStrippingEnabled__c = String.valueOf(false),
Scenario__c = firstMockScenario,
UserLoggingLevel__c = System.LoggingLevel.FINE.name()
);
LoggerScenarioRule.setMock(firstMockScenarioRule);
Logger.setScenario(firstMockScenario);
System.Assert.areEqual(firstMockScenario, Logger.getScenario());
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
String secondMockScenario = 'another test scenario ';
LoggerScenarioRule__mdt secondMockScenarioRule = new LoggerScenarioRule__mdt(
IsEnabled__c = true,
Scenario__c = secondMockScenario,
UserLoggingLevel__c = System.LoggingLevel.DEBUG.name()
);
LoggerScenarioRule.setMock(secondMockScenarioRule);
System.Assert.areNotEqual(firstMockScenarioRule.UserLoggingLevel__c, secondMockScenarioRule.UserLoggingLevel__c);
Logger.setScenario(secondMockScenario);
System.Assert.areEqual(secondMockScenario, Logger.getScenario());
System.Assert.areEqual(originalUserSettings.IsRecordFieldStrippingEnabled__c, Logger.getUserSettings().IsRecordFieldStrippingEnabled__c);
System.Assert.areEqual(secondMockScenarioRule.UserLoggingLevel__c, Logger.getUserLoggingLevel().name());

Logger.endScenario(secondMockScenario);

System.Assert.areEqual(firstMockScenario, Logger.getScenario());
System.Assert.areEqual(firstMockScenarioRule.UserLoggingLevel__c, Logger.getUserSettings().LoggingLevel__c);
System.Assert.areEqual(
Boolean.valueOf(firstMockScenarioRule.IsRecordFieldStrippingEnabled__c),
Logger.getUserSettings().IsRecordFieldStrippingEnabled__c
);
System.Assert.areEqual(originalUserSettings.IsEnabled__c, Logger.getUserSettings().IsEnabled__c);
System.Assert.areEqual(originalUserSettings.IsAnonymousModeEnabled__c, Logger.getUserSettings().IsAnonymousModeEnabled__c);
System.Assert.areEqual(originalUserSettings.IsApexSystemDebugLoggingEnabled__c, Logger.getUserSettings().IsApexSystemDebugLoggingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsDataMaskingEnabled__c, Logger.getUserSettings().IsDataMaskingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsJavaScriptConsoleLoggingEnabled__c, Logger.getUserSettings().IsJavaScriptConsoleLoggingEnabled__c);
System.Assert.areEqual(originalUserSettings.IsSavingEnabled__c, Logger.getUserSettings().IsSavingEnabled__c);
}

@IsTest
static void it_should_not_ignore_origin_when_apex_class_type_not_specified() {
// Don't bother testing stack trace logic when using a namespace prefix - there are
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nebula-logger",
"version": "4.13.15",
"version": "4.13.16",
"description": "The most robust logger for Salesforce. Works with Apex, Lightning Components, Flow, Process Builder & Integrations. Designed for Salesforce admins, developers & architects.",
"author": "Jonathan Gillespie",
"license": "MIT",
Expand Down
Loading

0 comments on commit 91f2eb2

Please sign in to comment.