From 1707c410386c5eea3b95de483428218835bb9dca Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Thu, 31 Oct 2024 18:58:06 +0000 Subject: [PATCH] CallableLogger Enhancements (#790) * Added 3 enhancements to the CallableLogger class (used for OmniStudio logging & loosely-coupled dependencies) 1. It now automatically appends OmniStudio's input data for OmniScript steps as JSON to the Message__c fields on LogEntryEvent__e and LogEntry__c. 2. The 'newEntry' action now also supports setting the parent log transaction ID, using the argument 'parentLogTransactionId'. 3. Transaction details are now returned in the output for all actions, including the current transaction ID, the parent log transaction ID, and the Salesforce-generated request ID. --- README.md | 10 +- .../demo_Logging_English_1.os-meta.xml | 382 +++++++++++++++++- docs/apex/Logger-Engine/Logger.md | 24 +- .../Test-Utilities/LoggerMockDataCreator.md | 4 +- .../Test-Utilities/LoggerMockDataStore.md | 20 + .../fields/ParentLogLink__c.field-meta.xml | 2 +- .../logger-engine/classes/CallableLogger.cls | 53 ++- .../main/logger-engine/classes/Logger.cls | 17 +- .../logger-engine/lwc/logger/loggerService.js | 2 +- .../classes/CallableLogger_Tests.cls | 162 ++++++-- .../logger-engine/classes/Logger_Tests.cls | 9 + package.json | 2 +- sfdx-project.json | 7 +- 13 files changed, 635 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 8be3a8377..4cb3cfc14 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,15 @@ The most robust observability solution for Salesforce experts. Built 100% natively on the platform, and designed to work seamlessly with Apex, Lightning Components, Flow, OmniStudio, and integrations. -## Unlocked Package - v4.14.15 +## Unlocked Package - v4.14.16 -[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015obxQAA) -[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015obxQAA) +[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocHQAQ) +[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y0000015ocHQAQ) [![View Documentation](./images/btn-view-documentation.png)](https://github.com/jongpie/NebulaLogger/wiki) -`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015obxQAA` +`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y0000015ocHQAQ` -`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015obxQAA` +`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y0000015ocHQAQ` --- diff --git a/config/scratch-orgs/omnistudio/omniScripts/demo_Logging_English_1.os-meta.xml b/config/scratch-orgs/omnistudio/omniScripts/demo_Logging_English_1.os-meta.xml index 03b52917c..193966fc7 100644 --- a/config/scratch-orgs/omnistudio/omniScripts/demo_Logging_English_1.os-meta.xml +++ b/config/scratch-orgs/omnistudio/omniScripts/demo_Logging_English_1.os-meta.xml @@ -9,6 +9,69 @@ true English Nebula Logger Demo + + true + false + 0.0 + Add Another New Log Entry with Remote Action + 0.0 + { + "controlWidth" : 12, + "label" : "", + "remoteClass" : "CallableLogger", + "remoteMethod" : "newEntry", + "remoteOptions" : { + "preTransformBundle" : "", + "postTransformBundle" : "" + }, + "remoteTimeout" : 30000, + "preTransformBundle" : "", + "postTransformBundle" : "", + "sendJSONPath" : "", + "sendJSONNode" : "", + "responseJSONPath" : "", + "responseJSONNode" : "", + "extraPayload" : { + "loggingLevel" : "INFO", + "message" : "another entry!", + "saveLog" : true + }, + "inProgressMessage" : "In Progress", + "postMessage" : "Done", + "failureNextLabel" : "Continue", + "failureAbortLabel" : "Abort", + "failureGoBackLabel" : "Go Back", + "failureAbortMessage" : "Are you sure?", + "validationRequired" : "Step", + "redirectPageName" : "", + "redirectTemplateUrl" : "vlcAcknowledge.html", + "redirectNextLabel" : "Next", + "redirectNextWidth" : 3, + "redirectPreviousLabel" : "Previous", + "redirectPreviousWidth" : 3, + "showPersistentComponent" : [ true, false ], + "show" : null, + "HTMLTemplateId" : "", + "wpm" : false, + "ssm" : false, + "message" : { }, + "pubsub" : false, + "svgSprite" : "", + "svgIcon" : "", + "errorMessage" : { + "custom" : [ ], + "default" : null + }, + "enableDefaultAbort" : false, + "enableActionMessage" : false, + "useContinuation" : false, + "businessCategory" : "", + "businessEvent" : "", + "toastComplete" : true +} + 3.0 + Remote Action + false false @@ -68,7 +131,7 @@ "businessCategory" : "", "businessEvent" : "" } - 1.0 + 4.0 Integration Procedure Action @@ -131,14 +194,323 @@ "businessEvent" : "", "toastComplete" : true } - 0.0 + 1.0 Remote Action + + + true + false + 1.0 + Checkbox1 + 0.0 + { + "controlWidth" : 12, + "label" : "Checkbox1", + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : false, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "checkLabel" : "Checkbox1", + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false +} + 0.0 + Checkbox + + + true + false + 1.0 + Date/Time1 + 0.0 + { + "controlWidth" : 12, + "label" : "Date/Time1", + "showInputWidth" : false, + "inputWidth" : 12, + "required" : false, + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : null, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "dateFormat" : "MM-dd-yyyy", + "timeFormat" : "hh:mm a", + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false, + "timezone" : "Local", + "minDate" : "", + "maxDate" : "", + "timeInterval" : 30 +} + 1.0 + Date/Time (Local) + + + true + false + 1.0 + Text1 + 0.0 + { + "controlWidth" : 12, + "label" : "Text1", + "showInputWidth" : false, + "inputWidth" : 12, + "required" : false, + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : null, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "mask" : "", + "pattern" : "", + "ptrnErrText" : "", + "minLength" : 0, + "maxLength" : 255, + "placeholder" : "", + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "debounceValue" : 0, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false, + "autocomplete" : null +} + 2.0 + Text + + true + false + 0.0 + Step1 + 0.0 + { + "label" : "Step1", + "validationRequired" : true, + "previousLabel" : "Previous", + "previousWidth" : 3, + "nextLabel" : "Next", + "nextWidth" : 3, + "cancelLabel" : "Cancel", + "cancelMessage" : "Are you sure?", + "saveLabel" : "Save for later", + "saveMessage" : "Are you sure you want to save it for later?", + "completeLabel" : "Complete", + "completeMessage" : "Are you sure you want to complete the script?", + "instruction" : "", + "showPersistentComponent" : [ true, false ], + "remoteClass" : "", + "remoteMethod" : "", + "remoteTimeout" : 30000, + "remoteOptions" : { }, + "knowledgeOptions" : { + "language" : "English", + "publishStatus" : "Online", + "keyword" : "", + "dataCategoryCriteria" : "", + "remoteTimeout" : 30000, + "typeFilter" : "" + }, + "show" : null, + "conditionType" : "Hide if False", + "HTMLTemplateId" : "", + "instructionKey" : "", + "chartLabel" : null, + "allowSaveForLater" : true, + "errorMessage" : { + "custom" : [ ], + "default" : null + }, + "wpm" : false, + "ssm" : false, + "message" : { }, + "pubsub" : false, + "businessCategory" : "", + "businessEvent" : "" +} + 0.0 + Step + + + + true + false + 1.0 + Checkbox2 + 0.0 + { + "controlWidth" : 12, + "label" : "Checkbox2", + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : false, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "checkLabel" : "Checkbox2", + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false +} + 2.0 + Checkbox + + + true + false + 1.0 + Currency1 + 0.0 + { + "controlWidth" : 12, + "label" : "Currency1", + "showInputWidth" : false, + "inputWidth" : 12, + "required" : false, + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : null, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "mask" : null, + "min" : null, + "max" : null, + "placeholder" : "", + "allowNegative" : false, + "hideGroupSep" : false, + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "debounceValue" : 0, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false, + "displayCurrencyCode" : false +} + 0.0 + Currency + + + true + false + 1.0 + Date1 + 0.0 + { + "controlWidth" : 12, + "label" : "Date1", + "showInputWidth" : false, + "inputWidth" : 12, + "required" : false, + "repeat" : false, + "repeatClone" : false, + "repeatLimit" : null, + "readOnly" : false, + "defaultValue" : null, + "help" : false, + "helpText" : "", + "helpTextPos" : "", + "dateType" : "string", + "modelDateFormat" : "yyyy-MM-dd", + "dateFormat" : "MM-dd-yyyy", + "show" : null, + "conditionType" : "Hide if False", + "accessibleInFutureSteps" : false, + "HTMLTemplateId" : "", + "hide" : false, + "disOnTplt" : false, + "minDate" : "", + "maxDate" : "" +} + 1.0 + Date + + true + false + 0.0 + Step2 + 0.0 + { + "label" : "Step2", + "validationRequired" : true, + "previousLabel" : "Previous", + "previousWidth" : 3, + "nextLabel" : "Next", + "nextWidth" : 3, + "cancelLabel" : "Cancel", + "cancelMessage" : "Are you sure?", + "saveLabel" : "Save for later", + "saveMessage" : "Are you sure you want to save it for later?", + "completeLabel" : "Complete", + "completeMessage" : "Are you sure you want to complete the script?", + "instruction" : "", + "showPersistentComponent" : [ true, false ], + "remoteClass" : "", + "remoteMethod" : "", + "remoteTimeout" : 30000, + "remoteOptions" : { }, + "knowledgeOptions" : { + "language" : "English", + "publishStatus" : "Online", + "keyword" : "", + "dataCategoryCriteria" : "", + "remoteTimeout" : 30000, + "typeFilter" : "" + }, + "show" : null, + "conditionType" : "Hide if False", + "HTMLTemplateId" : "", + "instructionKey" : "", + "chartLabel" : null, + "allowSaveForLater" : true, + "errorMessage" : { + "custom" : [ ], + "default" : null + }, + "wpm" : false, + "ssm" : false, + "message" : { }, + "pubsub" : false, + "businessCategory" : "", + "businessEvent" : "" +} + 2.0 + Step + OmniScript {"persistentComponent":[{"render":false,"label":"","remoteClass":"","remoteMethod":"","remoteTimeout":30000,"remoteOptions":{"preTransformBundle":"","postTransformBundle":""},"preTransformBundle":"","postTransformBundle":"","sendJSONPath":"","sendJSONNode":"","responseJSONPath":"","responseJSONNode":"","id":"vlcCart","itemsKey":"cartItems","modalConfigurationSetting":{"modalHTMLTemplateId":"vlcProductConfig.html","modalController":"ModalProductCtrl","modalSize":"lg"}},{"render":false,"dispOutsideOmni":false,"label":"","remoteClass":"","remoteMethod":"","remoteTimeout":30000,"remoteOptions":{"preTransformBundle":"","postTransformBundle":""},"preTransformBundle":"","postTransformBundle":"","id":"vlcKnowledge","itemsKey":"knowledgeItems","modalConfigurationSetting":{"modalHTMLTemplateId":"","modalController":"","modalSize":"lg"}}],"allowSaveForLater":true,"saveNameTemplate":null,"saveExpireInDays":null,"saveForLaterRedirectPageName":"sflRedirect","saveForLaterRedirectTemplateUrl":"vlcSaveForLaterAcknowledge.html","saveContentEncoded":false,"saveObjectId":"%ContextId%","saveURLPatterns":{},"autoSaveOnStepNext":false,"elementTypeToHTMLTemplateMapping":{},"seedDataJSON":{},"trackingCustomData":{},"enableKnowledge":false,"bLK":false,"lkObjName":null,"knowledgeArticleTypeQueryFieldsMap":{},"timeTracking":false,"hideStepChart":false,"mergeSavedData":false,"visualforcePagesAvailableInPreview":{},"cancelType":"SObject","allowCancel":true,"cancelSource":"%ContextId%","cancelRedirectPageName":"OmniScriptCancelled","cancelRedirectTemplateUrl":"vlcCancelled.html","consoleTabLabel":"New","wpm":false,"ssm":false,"message":{},"pubsub":false,"autoFocus":false,"currencyCode":"","showInputWidth":false,"rtpSeed":false,"consoleTabTitle":null,"consoleTabIcon":"custom:custom18","errorMessage":{"custom":[]},"stylesheet":{"newport":"","lightning":"","newportRtl":"","lightningRtl":""},"stepChartPlacement":"right","disableUnloadWarn":true,"scrollBehavior":"auto","currentLanguage":"en_US"} Logging demo - demo_Logging_English_6 - 6.0 - 533171c5-045f-0aab-f717-97eb6cd7e7f1 + demo_Logging_English_1 + 1.0 + 6a9d0ba6-c6de-2add-5f8e-bfba5391e74e diff --git a/docs/apex/Logger-Engine/Logger.md b/docs/apex/Logger-Engine/Logger.md index 0c2785cb6..bc6e0b56f 100644 --- a/docs/apex/Logger-Engine/Logger.md +++ b/docs/apex/Logger-Engine/Logger.md @@ -32,6 +32,12 @@ Boolean used when saving records. If true, all records must save correctly or an #### `childJobId` → `String` +#### `finalizerException` → `Exception` + +#### `finalizerResult` → `String` + +#### `finalizerUnhandledException` → `Map` + #### `location` → `String` #### `maintenanceWindow` → `String` @@ -3612,6 +3618,20 @@ String String - The parent log's transaction ID. This must be explicitly set by calling setParentLogTransactionId(String) +#### `getRequestId()` → `String` + +Returns the request ID generated by Salesforce for a particular transaction. This value is stored in `LogEntryEvent__e.RequestId__c` and `Log__c.RequestId__c`. + +##### Return + +**Type** + +String + +**Description** + +String - The value returned by `System.Request.getCurrent().getRequestId()` + #### `getSaveMethod()` → `SaveMethod` Returns the default save method used when calling saveLog() - set via LoggerSettings\_\_c or by calling setSaveMethod(SaveMethod saveMethod) @@ -3642,7 +3662,7 @@ The value currently set as the current transaction's scenario #### `getTransactionId()` → `String` -Returns the unique ID for a particular transaction, stored in Log**c.TransactionId**c +Returns the unique ID generated by Nebula Logger for a particular transaction. This value is stored in `LogEntryEvent__e.TransactionId__c` and `Log__c.TransactionId__c`. ##### Return @@ -3652,7 +3672,7 @@ String **Description** -String - The value of System.Request.getCurrent().getRequestId() +String - A `UUID` value generated by Nebula Logger, using `System.UUID.randomUUID().toString()` #### `getUserLoggingLevel()` → `System.LoggingLevel` diff --git a/docs/apex/Test-Utilities/LoggerMockDataCreator.md b/docs/apex/Test-Utilities/LoggerMockDataCreator.md index 0e2d5152c..5cc250661 100644 --- a/docs/apex/Test-Utilities/LoggerMockDataCreator.md +++ b/docs/apex/Test-Utilities/LoggerMockDataCreator.md @@ -576,6 +576,8 @@ A new copy of the original `SObject` record that has the specified read-only fie ###### `MockFinalizerContext(Id asyncApexJobId)` +###### `MockFinalizerContext(Exception ex)` + --- ##### Methods @@ -584,7 +586,7 @@ A new copy of the original `SObject` record that has the specified read-only fie ###### `getException()` → `Exception` -###### `getRequestId()` → `Id` +###### `getRequestId()` → `String` ###### `getResult()` → `System.ParentJobResult` diff --git a/docs/apex/Test-Utilities/LoggerMockDataStore.md b/docs/apex/Test-Utilities/LoggerMockDataStore.md index a8bf42754..2075d7bec 100644 --- a/docs/apex/Test-Utilities/LoggerMockDataStore.md +++ b/docs/apex/Test-Utilities/LoggerMockDataStore.md @@ -50,6 +50,26 @@ Utility class used to mock any data-related operations for the database, event b ###### `deliver(LoggerSObjectHandler sobjectHandlerInstance)` → `void` +###### `getMatchingPublishedPlatformEvents(SObject comparisonPlatformEvent)` → `List` + +Returns a list of published platform events that have the same field values as the provided platform event record `comparisonPlatformEvent`. This is useful for easily filtering to only the `LogEntryEvent__e` records relevant to a particular test method in a transaction/test scenario where multiple `LogEntryEvent__e` are being generated. Long-term, this helper method might be moved elsewhere, or replaced with something else, but for now, the mock event bus is a good-enough spot for it. + +####### Parameters + +| Param | Description | +| ------------------------- | ------------------------------------------------------------- | +| `comparisonPlatformEvent` | An instance of the platform event record to use for comparing | + +####### Return + +**Type** + +List<SObject> + +**Description** + +A list containing any matches. When no matches are found, the list is empty. + ###### `getPublishCallCount()` → `Integer` ###### `getPublishedPlatformEvents()` → `List` diff --git a/nebula-logger/core/main/log-management/objects/Log__c/fields/ParentLogLink__c.field-meta.xml b/nebula-logger/core/main/log-management/objects/Log__c/fields/ParentLogLink__c.field-meta.xml index 25b773c54..85ac7aa6e 100644 --- a/nebula-logger/core/main/log-management/objects/Log__c/fields/ParentLogLink__c.field-meta.xml +++ b/nebula-logger/core/main/log-management/objects/Log__c/fields/ParentLogLink__c.field-meta.xml @@ -6,7 +6,7 @@ false IF( NOT(ISBLANK(ParentLog__c)), - HYPERLINK(ParentLog__c, ParentLog__r.Name + ' - ' + ParentLogTransactionId__c), + HYPERLINK(ParentLog__c, ParentLog__r.Name + ' - ' + ParentLogTransactionId__c, '_top'), ParentLogTransactionId__c ) BlankAsZero diff --git a/nebula-logger/core/main/logger-engine/classes/CallableLogger.cls b/nebula-logger/core/main/logger-engine/classes/CallableLogger.cls index 54f87cafd..04dfcafd0 100644 --- a/nebula-logger/core/main/logger-engine/classes/CallableLogger.cls +++ b/nebula-logger/core/main/logger-engine/classes/CallableLogger.cls @@ -24,6 +24,7 @@ global without sharing class CallableLogger implements System.Callable { private static final String ARGUMENT_RECORD = 'record'; private static final String ARGUMENT_RECORD_LIST = 'recordList'; private static final String ARGUMENT_RECORD_MAP = 'recordMap'; + private static final String ARGUMENT_REQUEST_ID = 'requestId'; private static final String ARGUMENT_SAVE_LOG = 'saveLog'; private static final String ARGUMENT_SAVE_METHOD_NAME = 'saveMethodName'; private static final String ARGUMENT_TAGS = 'tags'; @@ -68,7 +69,7 @@ global without sharing class CallableLogger implements System.Callable { return output; } - @SuppressWarnings('PMD.ApexDoc') + @SuppressWarnings('PMD.ApexDoc, PMD.StdCyclomaticComplexity') private class CallableHandler { public void handleCall(String action, Map input, Map output) { switch on action { @@ -94,13 +95,19 @@ global without sharing class CallableLogger implements System.Callable { } // Methods for adding & saving log entries when 'newEntry' { - LogEntryEventBuilder builder = this.newEntry(input); - if (returnLogEntryEventBuilderInOutput) { - output.put(ARGUMENT_LOG_ENTRY_EVENT_BUILDER, builder); + if (input.containsKey(ARGUMENT_PARENT_LOG_TRANSACTION_ID)) { + String parentLogTransactionId = (String) input.get(ARGUMENT_PARENT_LOG_TRANSACTION_ID); + Logger.setParentLogTransactionId(parentLogTransactionId); } + + LogEntryEventBuilder builder = this.newEntry(input); if (input.get(ARGUMENT_SAVE_LOG) == true) { this.saveLog(input); } + + if (returnLogEntryEventBuilderInOutput) { + output.put(ARGUMENT_LOG_ENTRY_EVENT_BUILDER, builder); + } } when 'saveLog' { this.saveLog(input); @@ -109,17 +116,21 @@ global without sharing class CallableLogger implements System.Callable { throw new System.IllegalArgumentException('Unknown action: ' + action); } } + + output.put(ARGUMENT_TRANSACTION_ID, Logger.getTransactionId()); + output.put(ARGUMENT_PARENT_LOG_TRANSACTION_ID, Logger.getParentLogTransactionId()); + output.put(ARGUMENT_REQUEST_ID, Logger.getRequestId()); } - @SuppressWarnings('PMD.CognitiveComplexity, PMD.NcssMethodCount') + @SuppressWarnings('PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.NcssMethodCount') private LogEntryEventBuilder newEntry(Map input) { // The value of loggingLevel could be either a string name or an enum value from System.LoggingLevel, // so always first convert it to a string for consistency String loggingLevelName = input.get(ARGUMENT_LOGGING_LEVEL)?.toString(); System.LoggingLevel loggingLevel = Logger.getLoggingLevel(loggingLevelName); - String message = (String) input.get(ARGUMENT_MESSAGE); + String formattedMessage = CallableLogger.getFormattedMessage(input); - LogEntryEventBuilder logEntryEventBuilder = Logger.newEntry(loggingLevel, message); + LogEntryEventBuilder logEntryEventBuilder = Logger.newEntry(loggingLevel, formattedMessage); if (input.containsKey(ARGUMENT_EXCEPTION)) { logEntryEventBuilder.setExceptionDetails((System.Exception) input.get(ARGUMENT_EXCEPTION)); @@ -139,8 +150,9 @@ global without sharing class CallableLogger implements System.Callable { if (input.containsKey(ARGUMENT_TAGS)) { logEntryEventBuilder.addTags((List) input.get(ARGUMENT_TAGS)); } - if (input.containsKey(OMNISTUDIO_ARGUMENT_INPUT_OMNI_PROCESS_ID) || Logger.getCurrentQuiddity() == System.Quiddity.REMOTE_ACTION) { + if (CallableLogger.isOmniStudioInput(input)) { LogEntryEvent__e logEntryEvent = logEntryEventBuilder.getLogEntryEvent(); + logEntryEvent.OriginLocation__c = null; logEntryEvent.OriginSourceApiName__c = null; logEntryEvent.OriginSourceId__c = (String) input.get(OMNISTUDIO_ARGUMENT_INPUT_OMNI_PROCESS_ID); @@ -154,9 +166,32 @@ global without sharing class CallableLogger implements System.Callable { private void saveLog(Map input) { // The value of saveMethodName could be either a string name or an enum value from Logger.SaveMethod, // so always first convert it to a string for consistency - // System.Assert.fail(input.toString()); String saveMethodName = input.get(ARGUMENT_SAVE_METHOD_NAME)?.toString() ?? Logger.getUserSettings().DefaultSaveMethod__c; Logger.saveLog(saveMethodName); } } + + private static String getFormattedMessage(Map originalInput) { + String message = (String) originalInput.get(ARGUMENT_MESSAGE); + + if (CallableLogger.isOmniStudioInput(originalInput)) { + Map cleanedInput = new Map(); + for (String inputName : originalInput.keySet()) { + Object inputValue = originalInput.get(inputName); + if (inputValue instanceof Map) { + cleanedInput.put(inputName, inputValue); + } + } + + if (cleanedInput.keySet().isEmpty() == false) { + message += '\n\nOmniStudio Input:\n' + System.JSON.serializePretty(cleanedInput); + } + } + + return message; + } + + private static Boolean isOmniStudioInput(Map input) { + return input.containsKey(OMNISTUDIO_ARGUMENT_INPUT_OMNI_PROCESS_ID) || Logger.getCurrentQuiddity() == System.Quiddity.REMOTE_ACTION; + } } diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls index 5d6bc9926..515c713da 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.15'; + private static final String CURRENT_VERSION_NUMBER = 'v4.14.16'; 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.'; @@ -177,8 +177,19 @@ global with sharing class Logger { // Settings management methods /** - * @description Returns the unique ID for a particular transaction, stored in Log__c.TransactionId__c - * @return String - The value of System.Request.getCurrent().getRequestId() + * @description Returns the request ID generated by Salesforce for a particular transaction. + * This value is stored in `LogEntryEvent__e.RequestId__c` and `Log__c.RequestId__c`. + * @return String - The value returned by `System.Request.getCurrent().getRequestId()` + */ + // TODO decide if this should be made `global` in a future release + public static String getRequestId() { + return REQUEST_ID; + } + + /** + * @description Returns the unique ID generated by Nebula Logger for a particular transaction. + * This value is stored in `LogEntryEvent__e.TransactionId__c` and `Log__c.TransactionId__c`. + * @return String - A `UUID` value generated by Nebula Logger, using `System.UUID.randomUUID().toString()` */ global static String getTransactionId() { return TRANSACTION_ID; diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js index d885b3cb8..4cbcb0a94 100644 --- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js +++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js @@ -10,7 +10,7 @@ import LoggerServiceTaskQueue from './loggerServiceTaskQueue'; import getSettings from '@salesforce/apex/ComponentLogger.getSettings'; import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries'; -const CURRENT_VERSION_NUMBER = 'v4.14.15'; +const CURRENT_VERSION_NUMBER = 'v4.14.16'; const CONSOLE_OUTPUT_CONFIG = { messagePrefix: `%c Nebula Logger ${CURRENT_VERSION_NUMBER} `, diff --git a/nebula-logger/core/tests/logger-engine/classes/CallableLogger_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/CallableLogger_Tests.cls index 715d8d739..55aa1918a 100644 --- a/nebula-logger/core/tests/logger-engine/classes/CallableLogger_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/CallableLogger_Tests.cls @@ -22,7 +22,7 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call(fakeActionName, null); - System.Assert.areEqual(4, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(false, returnedOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + returnedOutput); System.Assert.areEqual('Unknown action: ' + fakeActionName, returnedOutput.get('exceptionMessage')); System.Assert.isNotNull(returnedOutput.get('exceptionStackTrace')); @@ -37,7 +37,7 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call(fakeActionName, new Map{ 'output' => omnistudioOutput }); - System.Assert.areEqual(4, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(false, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + omnistudioOutput); System.Assert.areEqual('Unknown action: ' + fakeActionName, omnistudioOutput.get('exceptionMessage')); System.Assert.isNotNull(omnistudioOutput.get('exceptionStackTrace')); @@ -54,7 +54,7 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call(incorrectlyCasedActionName, null); - System.Assert.areEqual(4, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(false, returnedOutput.get('isSuccess'), 'Expected isSuccess == false. Output received: ' + returnedOutput); System.Assert.areEqual('Unknown action: ' + incorrectlyCasedActionName, returnedOutput.get('exceptionMessage')); System.Assert.isNotNull(returnedOutput.get('exceptionStackTrace')); @@ -68,9 +68,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call('getTransactionId', null); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); } @IsTest @@ -81,9 +83,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('getTransactionId', new Map{ 'output' => omnistudioOutput }); - System.Assert.areEqual(2, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); } @IsTest @@ -95,9 +99,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call('getParentLogTransactionId', null); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); System.Assert.areEqual(mockParentLogTransactionId, returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); } @IsTest @@ -110,9 +116,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('getParentLogTransactionId', new Map{ 'output' => omnistudioOutput }); - System.Assert.areEqual(2, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); System.Assert.areEqual(mockParentLogTransactionId, omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); } @IsTest @@ -124,8 +132,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('setParentLogTransactionId', new Map{ 'parentLogTransactionId' => mockParentLogTransactionId }); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(mockParentLogTransactionId, Logger.getParentLogTransactionId()); } @@ -139,8 +150,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('setParentLogTransactionId', new Map{ 'input' => omnistudioInput, 'output' => omnistudioOutput }); - System.Assert.areEqual(1, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); System.Assert.areEqual(mockParentLogTransactionId, Logger.getParentLogTransactionId()); } @@ -153,8 +167,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call('getScenario', null); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(Logger.getScenario(), returnedOutput.get('scenario')); } @@ -168,9 +185,12 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('getScenario', new Map{ 'output' => omnistudioOutput }); - System.Assert.areEqual(2, omnistudioOutput.size()); + System.Assert.areEqual(5, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); System.Assert.areEqual(Logger.getScenario(), omnistudioOutput.get('scenario')); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); } @IsTest @@ -184,8 +204,11 @@ private class CallableLogger_Tests { new Map{ 'scenario' => mockScenario } ); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(mockScenario, Logger.getScenario()); } @@ -199,8 +222,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('setScenario', new Map{ 'input' => omnistudioInput, 'output' => omnistudioOutput }); - System.Assert.areEqual(1, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); System.Assert.areEqual(mockScenario, Logger.getScenario()); } @@ -216,8 +242,11 @@ private class CallableLogger_Tests { new Map{ 'scenario' => mockScenario } ); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.isNull(Logger.getScenario()); } @@ -232,8 +261,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('endScenario', new Map{ 'input' => omnistudioInput, 'output' => omnistudioOutput }); - System.Assert.areEqual(1, omnistudioOutput.size()); + System.Assert.areEqual(4, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); System.Assert.isNull(Logger.getScenario()); } @@ -248,8 +280,11 @@ private class CallableLogger_Tests { new Map{ 'loggingLevel' => loggingLevel, 'message' => message } ); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -272,8 +307,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('newEntry', new Map{ 'input' => omnistudioInput, 'output' => omnistudioOutput }); - System.Assert.areEqual(2, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput)); + System.Assert.areEqual(5, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) omnistudioOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevelName, logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -282,6 +320,44 @@ private class CallableLogger_Tests { System.Assert.areEqual('OmniStudio', logEntryEvent.OriginType__c); } + @IsTest + static void it_adds_omnistudio_output_maps_to_new_entry_message_when_using_omnistudio_approach() { + Logger.transactionQuiddity = System.Quiddity.REMOTE_ACTION; + String loggingLevelName = System.LoggingLevel.FINE.name(); + String message = 'some log entry message'; + Map firstStepInput = new Map{ 'TextInput1' => 'Value1', 'TextInput2' => 'Value2' }; + Map secondStepInput = new Map{ 'DateInput' => System.today(), 'BooleanInput' => true }; + Map omnistudioInput = new Map{ + 'loggingLevel' => loggingLevelName, + 'message' => message, + 'omniProcessId' => TEST_OMNI_PROCESS_ID, + 'someKeyForSomeString' => 'some string that should not be logged', + 'firstStepInput' => firstStepInput, + 'secondStepInput' => secondStepInput + }; + Map omnistudioOutput = new Map(); + + System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); + callableLoggerInstance.call('newEntry', new Map{ 'input' => omnistudioInput, 'output' => omnistudioOutput }); + + System.Assert.areEqual(5, omnistudioOutput.size(), System.JSON.serializePretty(omnistudioOutput.keySet())); + System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); + System.Assert.areEqual(Logger.getTransactionId(), omnistudioOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), omnistudioOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), omnistudioOutput.get('requestId')); + LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) omnistudioOutput.get('logEntryEventBuilder')).getLogEntryEvent(); + System.Assert.areEqual(loggingLevelName, logEntryEvent.LoggingLevel__c); + System.Assert.areEqual( + message + + '\n\nOmniStudio Input:\n' + + System.JSON.serializePretty(new Map{ 'firstStepInput' => firstStepInput, 'secondStepInput' => secondStepInput }), + logEntryEvent.Message__c + ); + System.Assert.isNull(logEntryEvent.OriginLocation__c, 'Expected a null for OriginLocation__c, received: ' + logEntryEvent.OriginLocation__c); + System.Assert.areEqual(TEST_OMNI_PROCESS_ID, logEntryEvent.OriginSourceId__c); + System.Assert.areEqual('OmniStudio', logEntryEvent.OriginType__c); + } + // For the other newEntry() tests, just test the standard approach - the OmniStudio approach will use the same parameters/inputs @IsTest static void it_adds_new_entry_with_exception_when_using_standard_approach() { @@ -295,8 +371,11 @@ private class CallableLogger_Tests { new Map{ 'exception' => apexException, 'loggingLevel' => loggingLevel, 'message' => message } ); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -321,8 +400,11 @@ private class CallableLogger_Tests { new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'recordId' => recordId } ); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -344,8 +426,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('newEntry', new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'record' => record }); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -367,8 +452,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('newEntry', new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'recordList' => recordList }); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -390,8 +478,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('newEntry', new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'recordMap' => recordMap }); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -413,8 +504,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('newEntry', new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'tags' => tags }); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -436,8 +530,11 @@ private class CallableLogger_Tests { Map returnedOutput = (Map) callableLoggerInstance .call('newEntry', new Map{ 'loggingLevel' => loggingLevel, 'message' => message, 'saveLog' => saveLog }); - System.Assert.areEqual(2, returnedOutput.size()); + System.Assert.areEqual(5, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); LogEntryEvent__e logEntryEvent = ((LogEntryEventBuilder) returnedOutput.get('logEntryEventBuilder')).getLogEntryEvent(); System.Assert.areEqual(loggingLevel.name(), logEntryEvent.LoggingLevel__c); System.Assert.areEqual(message, logEntryEvent.Message__c); @@ -456,8 +553,11 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); Map returnedOutput = (Map) callableLoggerInstance.call('saveLog', null); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(0, Logger.getBufferSize()); System.Assert.areEqual(Logger.getUserSettings().DefaultSaveMethod__c, Logger.lastSaveMethodNameUsed); } @@ -472,7 +572,7 @@ private class CallableLogger_Tests { System.Callable callableLoggerInstance = (System.Callable) System.Type.forName('CallableLogger').newInstance(); callableLoggerInstance.call('saveLog', new Map{ 'output' => omnistudioOutput }); - System.Assert.areEqual(1, omnistudioOutput.size(), omnistudioOutput.toString()); + System.Assert.areEqual(4, omnistudioOutput.size(), omnistudioOutput.toString()); System.Assert.areEqual(true, omnistudioOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + omnistudioOutput); System.Assert.areEqual(0, Logger.getBufferSize()); System.Assert.areEqual(Logger.getUserSettings().DefaultSaveMethod__c, Logger.lastSaveMethodNameUsed); @@ -491,8 +591,11 @@ private class CallableLogger_Tests { new Map{ 'saveMethodName' => targetSaveMethodName } ); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(0, Logger.getBufferSize()); System.Assert.areEqual(targetSaveMethodName, Logger.lastSaveMethodNameUsed); } @@ -510,8 +613,11 @@ private class CallableLogger_Tests { new Map{ 'saveMethodName' => targetSaveMethodName } ); - System.Assert.areEqual(1, returnedOutput.size()); + System.Assert.areEqual(4, returnedOutput.size(), System.JSON.serializePretty(returnedOutput.keySet())); System.Assert.areEqual(true, returnedOutput.get('isSuccess'), 'Expected isSuccess == true. Output received: ' + returnedOutput); + System.Assert.areEqual(Logger.getTransactionId(), returnedOutput.get('transactionId')); + System.Assert.areEqual(Logger.getParentLogTransactionId(), returnedOutput.get('parentLogTransactionId')); + System.Assert.areEqual(Logger.getRequestId(), returnedOutput.get('requestId')); System.Assert.areEqual(0, Logger.getBufferSize()); System.Assert.areEqual(targetSaveMethodName, Logger.lastSaveMethodNameUsed); } 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 1c7c0bd2d..1c44d10f3 100644 --- a/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls @@ -268,6 +268,15 @@ private class Logger_Tests { System.Assert.isNull(returnedSettings.Id); } + @IsTest + static void it_should_return_system_request_id() { + String expectedRequestId = System.Request.getCurrent().getRequestId(); + + String actualRequestId = Logger.getRequestId(); + + System.Assert.areEqual(expectedRequestId, actualRequestId); + } + @IsTest static void it_should_use_a_uuid_for_transaction_id() { String transactionId = Logger.getTransactionId(); diff --git a/package.json b/package.json index 979a6c713..0ab8ba275 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nebula-logger", - "version": "4.14.15", + "version": "4.14.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", diff --git a/sfdx-project.json b/sfdx-project.json index 47a477463..a90df6670 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.15.NEXT", - "versionName": "Create log entry for AsyncApexContext", - "versionDescription": "Adds additional details to the AsyncApexContext object being logged to expose System.Finalizer-specific details", + "versionNumber": "4.14.16.NEXT", + "versionName": "CallableLogger Enhancements", + "versionDescription": "Added 3 enhancements to the CallableLogger class (used for OmniStudio logging & loosely-coupled dependencies)\n\n 1. It now automatically appends OmniStudio's input data for OmniScript steps as JSON to the Message__c fields on LogEntryEvent__e and LogEntry__c.\n 2. The 'newEntry' action now also supports setting the parent log transaction ID, using the argument 'parentLogTransactionId'.\n 3. Transaction details are now returned in the output for all actions, including the current transaction ID, the parent log transaction ID, and the Salesforce-generated request ID.", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases", "unpackagedMetadata": { "path": "./nebula-logger/extra-tests" @@ -200,6 +200,7 @@ "Nebula Logger - Core@4.14.13-new-getlogger()-js-function": "04t5Y0000015oW3QAI", "Nebula Logger - Core@4.14.14-new-apex-static-method-&-javascript-function-logger.setfield()": "04t5Y0000015oWIQAY", "Nebula Logger - Core@4.14.15-create-log-entry-for-asyncapexcontext": "04t5Y0000015obxQAA", + "Nebula Logger - Core@4.14.16-callablelogger-enhancements": "04t5Y0000015ocHQAQ", "Nebula Logger - Core Plugin - Async Failure Additions": "0Ho5Y000000blO4SAI", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.0": "04t5Y0000015lhiQAA", "Nebula Logger - Core Plugin - Async Failure Additions@1.0.1": "04t5Y0000015lhsQAA",