diff --git a/README.md b/README.md
index eacf02963..cd07bddd4 100644
--- a/README.md
+++ b/README.md
@@ -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.2
+## Unlocked Package - v4.13.3
-[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkEmQAK)
-[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkEmQAK)
+[![Install Unlocked Package in a Sandbox](./images/btn-install-unlocked-package-sandbox.png)](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkEwQAK)
+[![Install Unlocked Package in Production](./images/btn-install-unlocked-package-production.png)](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t5Y000001MkEwQAK)
[![View Documentation](./images/btn-view-documentation.png)](https://jongpie.github.io/NebulaLogger/)
-`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkEmQAK`
+`sf package install --wait 20 --security-type AdminsOnly --package 04t5Y000001MkEwQAK`
-`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkEmQAK`
+`sfdx force:package:install --wait 20 --securitytype AdminsOnly --package 04t5Y000001MkEwQAK`
---
diff --git a/config/linters/lint-staged.config.js b/config/linters/lint-staged.config.js
index c48f715ac..1c05918bc 100644
--- a/config/linters/lint-staged.config.js
+++ b/config/linters/lint-staged.config.js
@@ -7,10 +7,11 @@ module.exports = {
return [`eslint --config ./config/linters/.eslintrc.json ${filenames.join(' ')} --fix`];
// FIXME this command should only run tests for the changed LWCs (instead of running tests for all LWCs)
// return [`eslint --config ./config/linters/.eslintrc.json ${filenames.join(' ')} --fix`, `npm run test:lwc`];
+ },
+ '*.{cls,trigger}': filenames => {
+ return filenames.map(
+ filename => `sf scanner run --pmdconfig ./config/linters/pmd-ruleset.xml --engine pmd --severity-threshold 3 --target '${filename}'`
+ );
+ // return [`npm run scan:apex`, `npm run docs:fix && git add ./docs/ && git commit --amend --no-edit`];
}
- // FIXME this command should only scan the changed Apex files (instead of scanning all Apex files)
- // '*.{cls,trigger}': () => {
- // return [`npm run scan:apex`];
- // // return [`npm run scan:apex`, `npm run docs:fix && git add ./docs/ && git commit --amend --no-edit`];
- // }
};
diff --git a/docs/apex/Configuration/LoggerParameter.md b/docs/apex/Configuration/LoggerParameter.md
index cc1f25232..8791a8ce5 100644
--- a/docs/apex/Configuration/LoggerParameter.md
+++ b/docs/apex/Configuration/LoggerParameter.md
@@ -94,6 +94,10 @@ Controls if Nebula Logger queries `Schema.User` data. When set to `false`, any `
Indicates if Nebula Logger queries `Schema.User` data is queried synchronously & populated on `LogEntryEvent__e` records. When set to `false`, any `Schema.User` fields on `LogEntryEvent__e` that rely on querying will not be populated - the data will instead be queried asynchronously and populated on any resulting `Log__c` records. Controlled by the custom metadata record `LoggerParameter.QueryUserDataSynchronously`, or `true` as the default
+#### `REQUIRE_SCENARIO_USAGE` → `Boolean`
+
+Indicates if Nebula Logger will enforce scenario-based logging to be used. When set to `false`, specifying a scenario is completely optional. When set to `true`, a scenario is required to be set before any logging can occur. If a logging method is called & the current scenario is null/blank, then Nebula Logger will throw a runtime exception. Controlled by the custom metadata record `LoggerParameter.RequireScenarioUsage`, or `false` as the default
+
#### `SEND_ERROR_EMAIL_NOTIFICATIONS` → `Boolean`
Indicates if Nebula Logger will send an error email notification if any internal exceptions occur. Controlled by the custom metadata record `LoggerParameter.SendErrorEmailNotifications`, or `true` as the default
diff --git a/docs/apex/Logger-Engine/Logger.md b/docs/apex/Logger-Engine/Logger.md
index 7a1c59cd4..8f0f15b44 100644
--- a/docs/apex/Logger-Engine/Logger.md
+++ b/docs/apex/Logger-Engine/Logger.md
@@ -5730,6 +5730,10 @@ The new entry's instance of `LogEntryEventBuilder`, useful for chaining met
### Inner Classes
+#### Logger.LoggerException class
+
+---
+
#### Logger.QueueableSaver class
Inner class for publishing log entries via the Queueable interface.
diff --git a/nebula-logger/core/main/configuration/classes/LoggerParameter.cls b/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
index 6b5b26e00..80831de63 100644
--- a/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
+++ b/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
@@ -335,6 +335,26 @@ public class LoggerParameter {
private set;
}
+ /**
+ * @description Indicates if Nebula Logger will enforce scenario-based logging to be used.
+ * When set to `false`, specifying a scenario is completely optional.
+ * When set to `true`, a scenario is required to be set before any logging can occur.
+ * If a logging method is called & the current scenario is null/blank, then Nebula Logger will throw a runtime exception.
+ * Controlled by the custom metadata record `LoggerParameter.RequireScenarioUsage`, or `false` as the default
+ */
+ public static final Boolean REQUIRE_SCENARIO_USAGE {
+ get {
+ if (REQUIRE_SCENARIO_USAGE == null) {
+ // Most features in Nebula Logger are enabled (true) by default,
+ // but this one is intentionally set to false by default - not
+ // all orgs want or need to use scenario-based logging
+ REQUIRE_SCENARIO_USAGE = getBoolean('RequireScenarioUsage', false);
+ }
+ return REQUIRE_SCENARIO_USAGE;
+ }
+ private set;
+ }
+
/**
* @description Indicates if Nebula Logger will send an error email notification if any internal exceptions occur.
* Controlled by the custom metadata record `LoggerParameter.SendErrorEmailNotifications`, or `true` as the default
diff --git a/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.RequireScenarioUsage.md-meta.xml b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.RequireScenarioUsage.md-meta.xml
new file mode 100644
index 000000000..20f5c61fa
--- /dev/null
+++ b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.RequireScenarioUsage.md-meta.xml
@@ -0,0 +1,19 @@
+
+
+
+ false
+
+ Description__c
+ When set to 'false' (default), specifying a scenario is completely optional.
+
+When set to 'true', a scenario is required to be set before any logging can occur. If a logging method is called & the current scenario is null/blank, then Nebula Logger will throw a runtime exception.
+
+
+ Value__c
+ false
+
+
diff --git a/nebula-logger/core/main/log-management/flexipages/LoggerScenarioRecordPage.flexipage-meta.xml b/nebula-logger/core/main/log-management/flexipages/LoggerScenarioRecordPage.flexipage-meta.xml
index bb645707e..ee1001a16 100644
--- a/nebula-logger/core/main/log-management/flexipages/LoggerScenarioRecordPage.flexipage-meta.xml
+++ b/nebula-logger/core/main/log-management/flexipages/LoggerScenarioRecordPage.flexipage-meta.xml
@@ -33,6 +33,21 @@
+
+ actionNames
+
+
+ Log__c.Manage
+
+
+
+
+ adminFilters
+
+
+ maxRecordsToDisplay
+ 30
+
parentFieldApiName
LoggerScenario__c.Id
@@ -42,19 +57,62 @@
Logs__r
- relatedListComponentOverride
+ relatedListDisplayType
ADVGRID
- rowsToDisplay
- 30
+ relatedListFieldAliases
+
+
+ NAME
+
+
+ StartTime__c
+
+
+ LoggedByUsernameLink__c
+
+
+ TransactionId__c
+
+
+ TotalLogEntries__c
+
+
+ TotalERRORLogEntries__c
+
+
+ TotalWARNLogEntries__c
+
+
+ OWNER.ALIAS
+
+
+ Priority__c
+
+
+ Status__c
+
+
+
+
+ relatedListLabel
+ Logs
showActionBar
true
- force:relatedListSingleContainer
- force_relatedListSingleContainer
+
+ sortFieldAlias
+ StartTime__c
+
+
+ sortFieldOrder
+ Descending
+
+ lst:dynamicRelatedList
+ lst_dynamicRelatedList
relatedTabContent
@@ -63,6 +121,18 @@
+
+ actionNames
+
+
+ MassChangeOwner
+
+
+
+
+ maxRecordsToDisplay
+ 30
+
parentFieldApiName
LoggerScenario__c.Id
@@ -72,19 +142,56 @@
LogEntries__r
- relatedListComponentOverride
+ relatedListDisplayType
ADVGRID
- rowsToDisplay
- 30
+ relatedListFieldAliases
+
+
+ NAME
+
+
+ Timestamp__c
+
+
+ LoggedByUsernameLink__c
+
+
+ Origin__c
+
+
+ Message__c
+
+
+ LoggingLevelWithImage__c
+
+
+ Log__c
+
+
+ RecordLink__c
+
+
+
+
+ relatedListLabel
+ Log Entries
showActionBar
- true
+ false
- force:relatedListSingleContainer
- force_relatedListSingleContainer3
+
+ sortFieldAlias
+ Timestamp__c
+
+
+ sortFieldOrder
+ Descending
+
+ lst:dynamicRelatedList
+ lst_dynamicRelatedList2
Facet-bf77ace2-6cee-4ca4-88b4-990633a74278
@@ -125,7 +232,7 @@
active
- true
+ false
body
@@ -141,6 +248,10 @@
+
+ active
+ false
+
body
Facet-bf77ace2-6cee-4ca4-88b4-990633a74278
diff --git a/nebula-logger/core/main/log-management/permissionsets/LoggerAdmin.permissionset-meta.xml b/nebula-logger/core/main/log-management/permissionsets/LoggerAdmin.permissionset-meta.xml
index 68f3f3ad8..cb8131fac 100644
--- a/nebula-logger/core/main/log-management/permissionsets/LoggerAdmin.permissionset-meta.xml
+++ b/nebula-logger/core/main/log-management/permissionsets/LoggerAdmin.permissionset-meta.xml
@@ -1558,6 +1558,11 @@
Log__c.TransactionScenarioText__c
true
+
+ false
+ Log__c.TransactionScenario__c
+ true
+
false
Log__c.UserLicenseDefinitionKey__c
diff --git a/nebula-logger/core/main/logger-engine/classes/Logger.cls b/nebula-logger/core/main/logger-engine/classes/Logger.cls
index da6f24f64..806ca1aa5 100644
--- a/nebula-logger/core/main/logger-engine/classes/Logger.cls
+++ b/nebula-logger/core/main/logger-engine/classes/Logger.cls
@@ -15,9 +15,10 @@
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.2';
+ private static final String CURRENT_VERSION_NUMBER = 'v4.13.3';
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.';
private static final String ORGANIZATION_DOMAIN_URL = System.URL.getOrgDomainUrl()?.toExternalForm();
private static final String REQUEST_ID = System.Request.getCurrent().getRequestId();
private static final Map SAVE_METHOD_NAME_TO_SAVE_METHOD = new Map();
@@ -3351,6 +3352,10 @@ global with sharing class Logger {
private static LogEntryEventBuilder newEntry(System.LoggingLevel loggingLevel, Boolean shouldSave) {
LogEntryEventBuilder logEntryEventBuilder = new LogEntryEventBuilder(getUserSettings(), loggingLevel, shouldSave);
if (logEntryEventBuilder.shouldSave()) {
+ if (LoggerParameter.REQUIRE_SCENARIO_USAGE && String.isBlank(currentEntryScenario)) {
+ throw new LoggerException(MISSING_SCENARIO_ERROR_MESSAGE);
+ }
+
LogEntryEvent__e logEntryEvent = logEntryEventBuilder.getLogEntryEvent();
logEntryEvent.ApiVersion__c = getOrganizationApiVersion();
logEntryEvent.EntryScenario__c = currentEntryScenario;
@@ -3493,6 +3498,11 @@ global with sharing class Logger {
}
}
+ // Inner class used for any exceptions specific to Nebula Logger functionality
+ @SuppressWarnings('PMD.ApexDoc')
+ public class LoggerException extends Exception {
+ }
+
// Inner class used for deserializing data from the status API
@SuppressWarnings('PMD.ApexDoc, PMD.VariableNamingConventions')
public class StatusApiResponse {
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 6212e8c4d..9236b1756 100644
--- a/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js
+++ b/nebula-logger/core/main/logger-engine/lwc/logger/logEntryBuilder.js
@@ -4,7 +4,7 @@
//------------------------------------------------------------------------------------------------//
import FORM_FACTOR from '@salesforce/client/formFactor';
-const CURRENT_VERSION_NUMBER = 'v4.13.2';
+const CURRENT_VERSION_NUMBER = 'v4.13.3';
// JavaScript equivalent to the Apex class ComponentLogger.ComponentLogEntry
const ComponentLogEntry = class {
diff --git a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
index c65031596..fef755e5e 100644
--- a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
+++ b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
@@ -272,6 +272,33 @@ private class LoggerParameter_Tests {
System.Assert.areEqual(mockValue, returnedValue);
}
+ @IsTest
+ static void it_should_use_false_as_default_constant_value_for_require_scenario_usage_when_null() {
+ LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'RequireScenarioUsage', Value__c = null));
+
+ Boolean returnedValue = LoggerParameter.REQUIRE_SCENARIO_USAGE;
+
+ System.Assert.isFalse(returnedValue);
+ }
+
+ @IsTest
+ static void it_should_return_constant_value_for_require_scenario_usage_when_true() {
+ LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'RequireScenarioUsage', Value__c = JSON.serialize(true)));
+
+ Boolean returnedValue = LoggerParameter.REQUIRE_SCENARIO_USAGE;
+
+ System.Assert.isTrue(returnedValue);
+ }
+
+ @IsTest
+ static void it_should_return_constant_value_for_require_scenario_usage_when_false() {
+ LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'RequireScenarioUsage', Value__c = JSON.serialize(false)));
+
+ Boolean returnedValue = LoggerParameter.REQUIRE_SCENARIO_USAGE;
+
+ System.Assert.isFalse(returnedValue);
+ }
+
@IsTest
static void it_should_return_constant_value_for_send_error_email_notifications() {
Boolean mockValue = false;
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 f75d9f5e5..ee0ca5a90 100644
--- a/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls
+++ b/nebula-logger/core/tests/logger-engine/classes/Logger_Tests.cls
@@ -325,6 +325,25 @@ private class Logger_Tests {
System.Assert.areEqual(mockScenario, Logger.getScenario(), 'The mock scenario should have been used as the current transaction scenario');
}
+ @IsTest
+ static void it_should_require_scenario_when_parameter_enabled() {
+ LoggerParameter__mdt mockParameter = new LoggerParameter__mdt(DeveloperName = 'RequireScenarioUsage', Value__c = String.valueOf(true));
+ LoggerParameter.setMock(mockParameter);
+ System.Assert.isTrue(LoggerParameter.REQUIRE_SCENARIO_USAGE);
+ String nullScenario = null;
+
+ Logger.setScenario(nullScenario);
+ Exception thrownException;
+ try {
+ Logger.info('Some message');
+ } catch (Exception ex) {
+ thrownException = ex;
+ }
+
+ System.Assert.isNotNull(thrownException, 'An exception should have been thrown due to a null scenario');
+ System.Assert.isInstanceOfType(thrownException, Logger.LoggerException.class);
+ }
+
@IsTest
static void it_should_use_the_first_specified_scenario_as_transaction_scenario_when_parameter_is_true() {
LoggerDataStore.setMock(LoggerMockDataStore.getEventBus());
diff --git a/package.json b/package.json
index 15ee1f6ca..0bb6e5a49 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "nebula-logger",
- "version": "4.13.2",
+ "version": "4.13.3",
"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",
@@ -33,7 +33,7 @@
},
"scripts": {
"apex:tail:log": "sf apex tail log --color",
- "apex:tail:log:file": "sf apex tail log | tee sf_tail.log",
+ "apex:tail:log:file": "sh scripts/build/tail-log-to-file.sh",
"devhub:details": "pwsh ./scripts/build/get-devhub-org-details.ps1",
"devhub:limits": "pwsh ./scripts/build/get-devhub-org-limits.ps1",
"devhub:open": "pwsh ./scripts/build/open-devhub-org.ps1",
diff --git a/scripts/build/tail-log-to-file.sh b/scripts/build/tail-log-to-file.sh
new file mode 100644
index 000000000..8f68dca82
--- /dev/null
+++ b/scripts/build/tail-log-to-file.sh
@@ -0,0 +1,6 @@
+now=$(date +"%Y-%m-%dT%H_%M_%S%z")
+logDirectory="logs"
+
+echo "Starting sf apex tail log at ${now}"
+[[ -d ${logDirectory} ]] || mkdir ${logDirectory}
+sf apex tail log --color | tee ./${logDirectory}/sf_tail_${now}.log
diff --git a/sfdx-project.json b/sfdx-project.json
index d5b1e9c53..7287749f7 100644
--- a/sfdx-project.json
+++ b/sfdx-project.json
@@ -14,9 +14,9 @@
"path": "./nebula-logger/core",
"definitionFile": "./config/scratch-orgs/base-scratch-def.json",
"scopeProfiles": true,
- "versionNumber": "4.13.2.NEXT",
- "versionName": "Capture Organization Limits Usage",
- "versionDescription": "Added the ability to capture & view organization limits on Log__c with new LWC logOrganizationLimits, as well as the ability to toggle capturing of transaction limits with a new LoggerParameter__mdt record",
+ "versionNumber": "4.13.3.NEXT",
+ "versionName": "Optionally Enforce Scenario Usage",
+ "versionDescription": "Added the ability to require scenario-based logging, using a new LoggerParameter__mdt record 'RequireScenarioUsage'",
"releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases",
"unpackagedMetadata": {
"path": "./nebula-logger/extra-tests"
@@ -170,6 +170,7 @@
"Nebula Logger - Core@4.13.0-spring-'24-release": "04t5Y000001Mk8dQAC",
"Nebula Logger - Core@4.13.1-apex-observability": "04t5Y000001MkE3QAK",
"Nebula Logger - Core@4.13.2-capture-organization-limits-usage": "04t5Y000001MkEmQAK",
+ "Nebula Logger - Core@4.13.3-optionally-enforce-scenario-usage": "04t5Y000001MkEwQAK",
"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",