From 81b5bcedf7c4e701ab3317b1974e35e74abf195e Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Fri, 20 Sep 2024 00:32:35 -0400 Subject: [PATCH] Updated the behavior of Logger.setAsyncContext() to only set the context the first time a non-null context value is provided --- README.md | 2 +- .../main/logger-engine/classes/Logger.cls | 27 +++- .../lwc/logger/logEntryBuilder.js | 2 +- .../logger-engine/classes/Logger_Tests.cls | 128 ++++++++++++++++++ package.json | 2 +- sfdx-project.json | 6 +- 6 files changed, 154 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 06897fbec..ba7015a35 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, Process Builder & integrations. -## Unlocked Package - v4.14.10 +## Unlocked Package - v4.14.11 [![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oTdQAI) [![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015oTdQAI) diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index 9fa54941a..f73418b4c 100644 --- a/nebula-logger/core/main/logger-engine/classes/Logger.cls +++ b/nebula-logger/core/main/logger-engine/classes/Logger.cls @@ -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.14.10'; + private static final String CURRENT_VERSION_NUMBER = 'v4.14.11'; private static final System.LoggingLevel FALLBACK_LOGGING_LEVEL = System.LoggingLevel.DEBUG; private static final List LOG_ENTRIES_BUFFER = new List(); private static final String MISSING_SCENARIO_ERROR_MESSAGE = 'No logger scenario specified. A scenario is required for logging in this org.'; @@ -196,7 +196,9 @@ global with sharing class Logger { * @param batchableContext - The instance of `Database.BatchableContext` to track */ global static void setAsyncContext(Database.BatchableContext batchableContext) { - setAsyncContext(new AsyncContext(batchableContext)); + if (batchableContext != null) { + setAsyncContext(new AsyncContext(batchableContext)); + } } /** @@ -204,7 +206,9 @@ global with sharing class Logger { * @param finalizerContext - The instance of `System.FinalizerContext` to track */ global static void setAsyncContext(System.FinalizerContext finalizerContext) { - setAsyncContext(new AsyncContext(finalizerContext)); + if (finalizerContext != null) { + setAsyncContext(new AsyncContext(finalizerContext)); + } } /** @@ -212,7 +216,9 @@ global with sharing class Logger { * @param queueableContext - The instance of `System.QueueableContext` to track */ global static void setAsyncContext(System.QueueableContext queueableContext) { - setAsyncContext(new AsyncContext(queueableContext)); + if (queueableContext != null) { + setAsyncContext(new AsyncContext(queueableContext)); + } } /** @@ -220,7 +226,9 @@ global with sharing class Logger { * @param schedulableContext - The instance of `System.SchedulableContext` to track */ global static void setAsyncContext(System.SchedulableContext schedulableContext) { - setAsyncContext(new AsyncContext(schedulableContext)); + if (schedulableContext != null) { + setAsyncContext(new AsyncContext(schedulableContext)); + } } /** @@ -3415,8 +3423,13 @@ global with sharing class Logger { } private static void setAsyncContext(AsyncContext asyncContext) { - currentAsyncContext = asyncContext; - System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Async Context: ' + System.JSON.serializePretty(asyncContext)); + // Only set the async context the first time that a non-null value is provided + // Previous versions of Nebula Logger would always set it, but that wasn't the + // intended behavior + if (currentAsyncContext == null) { + currentAsyncContext = asyncContext; + System.debug(System.LoggingLevel.INFO, 'Nebula Logger - Async Context: ' + System.JSON.serializePretty(asyncContext)); + } } private static SaveMethod getSaveMethod(String saveMethodName) { diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js index 1150bda94..2a964b65b 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js @@ -6,7 +6,7 @@ import FORM_FACTOR from '@salesforce/client/formFactor'; import { log as lightningLog } from 'lightning/logger'; import { LoggerStackTrace } from './loggerStackTrace'; -const CURRENT_VERSION_NUMBER = 'v4.14.10'; +const CURRENT_VERSION_NUMBER = 'v4.14.11'; const LOGGING_LEVEL_EMOJIS = { ERROR: '⛔', diff --git a/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls index 798db7562..9fff107c1 100644 --- a/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls @@ -792,6 +792,39 @@ private class Logger_Tests { System.Assert.areEqual(Database.BatchableContext.class.getName(), logEntryEvent.AsyncContextType__c); } + @IsTest + static void it_should_use_first_non_null_context_details_for_batchable_context_when_event_published() { + Database.BatchableContext nullMockContext = null; + Database.BatchableContext firstNonNullMockContext = new LoggerMockDataCreator.MockBatchableContext(); + Database.BatchableContext secondNonNullMockContext = new LoggerMockDataCreator.MockBatchableContext(); + System.Assert.areNotEqual( + firstNonNullMockContext, + secondNonNullMockContext, + 'Test has started under the wrong conditions, expected 2 different mock contexts' + ); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + LogEntryEvent__e logEntryEvent = Logger.info('hello, world').getLogEntryEvent(); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.isNull(logEntryEvent.AsyncContextType__c); + System.Assert.areEqual(1, Logger.getBufferSize()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + Logger.setAsyncContext(nullMockContext); + Logger.setAsyncContext(firstNonNullMockContext); + Logger.setAsyncContext(secondNonNullMockContext); + Logger.saveLog(Logger.SaveMethod.EVENT_BUS); + + System.Assert.areEqual(0, Logger.getBufferSize()); + System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + System.Assert.areEqual(logEntryEvent, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0)); + System.Assert.areEqual(firstNonNullMockContext.getChildJobId(), logEntryEvent.AsyncContextChildJobId__c); + System.Assert.areEqual(firstNonNullMockContext.getJobId(), logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.areEqual(Database.BatchableContext.class.getName(), logEntryEvent.AsyncContextType__c); + } + @IsTest static void it_should_set_async_context_details_for_finalizer_context_when_event_published() { Id mockParentAsyncApexJobId = LoggerMockDataCreator.createId(Schema.AsyncApexJob.SObjectType); @@ -818,6 +851,39 @@ private class Logger_Tests { System.Assert.areEqual(System.FinalizerContext.class.getName(), logEntryEvent.AsyncContextType__c); } + @IsTest + static void it_should_use_first_non_null_context_details_for_finalizer_context_when_event_published() { + System.FinalizerContext nullMockContext = null; + System.FinalizerContext firstNonNullMockContext = new LoggerMockDataCreator.MockFinalizerContext(); + System.FinalizerContext secondNonNullMockContext = new LoggerMockDataCreator.MockFinalizerContext(); + System.Assert.areNotEqual( + firstNonNullMockContext, + secondNonNullMockContext, + 'Test has started under the wrong conditions, expected 2 different mock contexts' + ); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + LogEntryEvent__e logEntryEvent = Logger.info('hello, world').getLogEntryEvent(); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.isNull(logEntryEvent.AsyncContextType__c); + System.Assert.areEqual(1, Logger.getBufferSize()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + Logger.setAsyncContext(nullMockContext); + Logger.setAsyncContext(firstNonNullMockContext); + Logger.setAsyncContext(secondNonNullMockContext); + Logger.saveLog(Logger.SaveMethod.EVENT_BUS); + + System.Assert.areEqual(0, Logger.getBufferSize()); + System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + System.Assert.areEqual(logEntryEvent, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0)); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.areEqual(firstNonNullMockContext.getAsyncApexJobId(), logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.areEqual(System.FinalizerContext.class.getName(), logEntryEvent.AsyncContextType__c); + } + @IsTest static void it_should_set_async_context_details_for_queueable_context_when_event_published() { Id mockParentAsyncApexJobId = LoggerMockDataCreator.createId(Schema.AsyncApexJob.SObjectType); @@ -844,6 +910,39 @@ private class Logger_Tests { System.Assert.areEqual(System.QueueableContext.class.getName(), logEntryEvent.AsyncContextType__c); } + @IsTest + static void it_should_use_first_non_null_context_details_for_queueable_context_when_event_published() { + System.QueueableContext nullMockContext = null; + System.QueueableContext firstNonNullMockContext = new LoggerMockDataCreator.MockQueueableContext(); + System.QueueableContext secondNonNullMockContext = new LoggerMockDataCreator.MockQueueableContext(); + System.Assert.areNotEqual( + firstNonNullMockContext, + secondNonNullMockContext, + 'Test has started under the wrong conditions, expected 2 different mock contexts' + ); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + LogEntryEvent__e logEntryEvent = Logger.info('hello, world').getLogEntryEvent(); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.isNull(logEntryEvent.AsyncContextType__c); + System.Assert.areEqual(1, Logger.getBufferSize()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + Logger.setAsyncContext(nullMockContext); + Logger.setAsyncContext(firstNonNullMockContext); + Logger.setAsyncContext(secondNonNullMockContext); + Logger.saveLog(Logger.SaveMethod.EVENT_BUS); + + System.Assert.areEqual(0, Logger.getBufferSize()); + System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + System.Assert.areEqual(logEntryEvent, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0)); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.areEqual(firstNonNullMockContext.getJobId(), logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.areEqual(System.QueueableContext.class.getName(), logEntryEvent.AsyncContextType__c); + } + @IsTest static void it_should_set_async_context_details_for_schedulable_context_when_event_published() { Id mockCronTriggerId = LoggerMockDataCreator.createId(Schema.CronTrigger.SObjectType); @@ -869,6 +968,35 @@ private class Logger_Tests { System.Assert.areEqual(System.SchedulableContext.class.getName(), logEntryEvent.AsyncContextType__c); } + @IsTest + static void it_should_use_first_non_null_context_details_for_schedulable_context_when_event_published() { + System.SchedulableContext nullMockContext = null; + System.SchedulableContext firstNonNullMockContext = new LoggerMockDataCreator.MockSchedulableContext(); + System.SchedulableContext secondNonNullMockContext = new LoggerMockDataCreator.MockSchedulableContext(); + System.Assert.areNotEqual(firstNonNullMockContext, secondNonNullMockContext, 'Test has started under the wrong conditions, expected 2 different mock contexts'); + LoggerDataStore.setMock(LoggerMockDataStore.getEventBus()); + LogEntryEvent__e logEntryEvent = Logger.info('hello, world').getLogEntryEvent(); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextParentJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextTriggerId__c); + System.Assert.isNull(logEntryEvent.AsyncContextType__c); + System.Assert.areEqual(1, Logger.getBufferSize()); + System.Assert.areEqual(0, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + + Logger.setAsyncContext(nullMockContext); + Logger.setAsyncContext(firstNonNullMockContext); + Logger.setAsyncContext(secondNonNullMockContext); + Logger.saveLog(Logger.SaveMethod.EVENT_BUS); + + System.Assert.areEqual(0, Logger.getBufferSize()); + System.Assert.areEqual(1, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().size()); + System.Assert.areEqual(logEntryEvent, LoggerMockDataStore.getEventBus().getPublishedPlatformEvents().get(0)); + System.Assert.isNull(logEntryEvent.AsyncContextChildJobId__c); + System.Assert.isNull(logEntryEvent.AsyncContextParentJobId__c); + System.Assert.areEqual(firstNonNullMockContext.getTriggerId(), logEntryEvent.AsyncContextTriggerId__c); + System.Assert.areEqual(System.SchedulableContext.class.getName(), logEntryEvent.AsyncContextType__c); + } + @IsTest static void it_should_set_parent_transaction_id() { String expectedParentTransactionId = 'imagineThisWereAGuid'; diff --git a/package.json b/package.json index 4aaf72cdf..d28f0159d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nebula-logger", - "version": "4.14.10", + "version": "4.14.11", "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", diff --git a/sfdx-project.json b/sfdx-project.json index 0169bbefc..6e1d3cc75 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -9,9 +9,9 @@ "path": "./nebula-logger/core", "definitionFile": "./config/scratch-orgs/base-scratch-def.json", "scopeProfiles": true, - "versionNumber": "4.14.10.NEXT", - "versionName": "New CallableLogger Apex class", - "versionDescription": "Added a new CallableLogger class that provides support for both OmniStudio logging, as well as the ability to dynamically call Nebula Logger in Apex when it's available", + "versionNumber": "4.14.11.NEXT", + "versionName": "Updated Behavior of Logger.setAsyncContext()", + "versionDescription": "Updated the behavior of Logger.setAsyncContext() to only set the context the first time a non-null context value is provided. Previously, subsequent calls would overwrite the context value, which wasn't really the intended behaviour.", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases", "unpackagedMetadata": { "path": "./nebula-logger/extra-tests"