diff --git a/nebula-logger/core.package.xml b/nebula-logger/core.package.xml
index 9118c4950..87a7bfae9 100644
--- a/nebula-logger/core.package.xml
+++ b/nebula-logger/core.package.xml
@@ -735,6 +735,7 @@
LoggerParameter.EnableLoggerSystemMessages
LoggerParameter.EnableStackTraceParsing
LoggerParameter.EnableTagging
+ LoggerParameter.IgnoredJavaScriptOrigins
LoggerParameter.LogBatchPurgerDefaultBatchSize
LoggerParameter.LogEntryEventStreamDisplayFields
LoggerParameter.NormalizeScenarioData
diff --git a/nebula-logger/core/main/configuration/classes/LoggerParameter.cls b/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
index 4d53ec202..eab16a119 100644
--- a/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
+++ b/nebula-logger/core/main/configuration/classes/LoggerParameter.cls
@@ -102,6 +102,22 @@ public class LoggerParameter {
private set;
}
+ /**
+ * @description A list of JavaScript file name patterns that should be ignored when parsing stack traces.
+ * Any stack trace lines containing file names that match these patterns will be removed from the parsed stack trace.
+ * This is useful for filtering out utility or framework files from stack traces.
+ * Controlled by the custom metadata record `LoggerParameter.IgnoredJavaScriptOrigins`, or an empty list as the default
+ */
+ public static final List IGNORED_JAVASCRIPT_ORIGINS {
+ get {
+ if (IGNORED_JAVASCRIPT_ORIGINS == null) {
+ IGNORED_JAVASCRIPT_ORIGINS = getStringList('IgnoredJavaScriptOrigins', new List());
+ }
+ return IGNORED_JAVASCRIPT_ORIGINS;
+ }
+ private set;
+ }
+
/**
* @description Controls the default batch size used by the batchable class `LogBatchPurger` when purging old logging data.
* Controlled by the custom metadata record `LoggerParameter.LogBatchPurgerDefaultBatchSize`, or `500` as the default
diff --git a/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredJavaScriptOrigins.md-meta.xml b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredJavaScriptOrigins.md-meta.xml
new file mode 100644
index 000000000..c67b5b10f
--- /dev/null
+++ b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredJavaScriptOrigins.md-meta.xml
@@ -0,0 +1,19 @@
+
+
+
+ false
+
+ Comments__c
+
+
+
+ Description__c
+ A comma-separated list of JavaScript file name patterns to ignore when parsing stack traces. For example: 'jquery,analytics.js,third-party-lib'
+
+These patterns will be used to filter out utility libraries, third-party scripts, or other files that should not appear in stack traces. This helps clean up JavaScript stack traces for better readability and debugging.
+
+
+ Value__c
+
+
+
\ No newline at end of file
diff --git a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls
index 6cb3b14bb..ce1113a7c 100644
--- a/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls
+++ b/nebula-logger/core/main/logger-engine/classes/ComponentLogger.cls
@@ -249,6 +249,13 @@ public inherited sharing class ComponentLogger {
@AuraEnabled
public ComponentLoggingLevel userLoggingLevel { get; set; }
+ /**
+ * @description A list of JavaScript file name patterns that should be ignored when parsing stack traces,
+ * based on `LoggerParameter.IgnoredJavaScriptOrigins`
+ */
+ @AuraEnabled
+ public List ignoredJavaScriptOrigins { get; set; }
+
private ComponentLoggerSettings() {
LoggerSettings__c userSettings = Logger.getUserSettings();
@@ -258,6 +265,7 @@ public inherited sharing class ComponentLogger {
this.isLightningLoggerEnabled = userSettings.IsJavaScriptLightningLoggerEnabled__c;
this.supportedLoggingLevels = getSupportedLoggingLevels();
this.userLoggingLevel = getUserLoggingLevel();
+ this.ignoredJavaScriptOrigins = LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS;
}
private Map getSupportedLoggingLevels() {
diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js
index 461885e5a..74aeed327 100644
--- a/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js
+++ b/nebula-logger/core/main/logger-engine/lwc/logger/__tests__/loggerStackTrace.test.js
@@ -168,4 +168,45 @@ describe('logger stack trace parsing tests', () => {
expect(originStackTrace.functionName).toEqual('logInfoWithoutDebugExample');
expect(originStackTrace.metadataType).toEqual('LightningComponentBundle');
});
+
+ it('correctly applies instance-level ignored origins', async () => {
+ const loggerStackTrace = new LoggerStackTrace();
+ loggerStackTrace.setIgnoredOrigins(['jquery', 'analytics']);
+
+ // Create a mock error with a stack trace that includes ignored origins
+ const mockError = new Error('Test error');
+ mockError.stack = `Error: Test error
+ at someFunction (https://example.com/jquery.min.js:1:1)
+ at anotherFunction (https://example.com/analytics.js:2:2)
+ at validFunction (https://example.com/myComponent.js:3:3)`;
+
+ const originStackTrace = loggerStackTrace.parse(mockError);
+
+ // Should skip the ignored files and use the valid one
+ expect(originStackTrace.fileName).toContain('myComponent.js');
+ expect(originStackTrace.fileName).not.toContain('jquery');
+ expect(originStackTrace.fileName).not.toContain('analytics');
+ });
+
+ it('correctly applies global ignored origins', async () => {
+ // Set global ignored origins
+ LoggerStackTrace.setGlobalIgnoredOrigins(['global-ignored']);
+
+ const loggerStackTrace = new LoggerStackTrace();
+
+ // Create a mock error with a stack trace that includes globally ignored origins
+ const mockError = new Error('Test error');
+ mockError.stack = `Error: Test error
+ at someFunction (https://example.com/global-ignored.js:1:1)
+ at validFunction (https://example.com/myComponent.js:3:3)`;
+
+ const originStackTrace = loggerStackTrace.parse(mockError);
+
+ // Should skip the globally ignored file and use the valid one
+ expect(originStackTrace.fileName).toContain('myComponent.js');
+ expect(originStackTrace.fileName).not.toContain('global-ignored');
+
+ // Clean up global state
+ LoggerStackTrace.setGlobalIgnoredOrigins([]);
+ });
});
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 cda68b59f..065deddf7 100644
--- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js
+++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerService.js
@@ -7,6 +7,7 @@ import FORM_FACTOR from '@salesforce/client/formFactor';
import { log as lightningLog } from 'lightning/logger';
import LogEntryEventBuilder from './logEntryBuilder';
import LoggerServiceTaskQueue from './loggerServiceTaskQueue';
+import LoggerStackTrace from './loggerStackTrace';
import getSettings from '@salesforce/apex/ComponentLogger.getSettings';
import saveComponentLogEntries from '@salesforce/apex/ComponentLogger.saveComponentLogEntries';
@@ -70,6 +71,14 @@ export default class LoggerService {
this.#scenario = scenario;
}
+ /**
+ * @description Sets the list of JavaScript file name patterns to ignore when parsing stack traces
+ * @param {String[]} ignoredOrigins - Array of file name patterns to ignore (e.g., ['jquery', 'analytics.js'])
+ */
+ setIgnoredOrigins(ignoredOrigins) {
+ LoggerStackTrace.setGlobalIgnoredOrigins(ignoredOrigins);
+ }
+
exception(message, exception, originStackTraceError) {
this.error(message, originStackTraceError).setExceptionDetails(exception);
this.saveLog();
@@ -156,6 +165,9 @@ export default class LoggerService {
userLoggingLevel: Object.freeze(retrievedSettings.userLoggingLevel)
});
+ // Configure LoggerStackTrace with ignored origins from settings
+ LoggerStackTrace.setGlobalIgnoredOrigins(this.#settings.ignoredJavaScriptOrigins);
+
if (!LoggerService.hasInitialized && LoggerService.areSystemMessagesEnabled && this.#settings.isConsoleLoggingEnabled) {
this._logToConsole('INFO', 'logger component initialized\n' + JSON.stringify(new BrowserContext(), null, 2));
}
diff --git a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js
index 9a252e4e4..55ddd3b50 100644
--- a/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js
+++ b/nebula-logger/core/main/logger-engine/lwc/logger/loggerStackTrace.js
@@ -149,6 +149,31 @@ The code below is specific to Nebula Logger - it leverages stacktrace.js plus so
additional parsing logic to handle Salesforce-specific stack traces in LWC & Aura components
*/
export default class LoggerStackTrace {
+ static #globalIgnoredOrigins = [];
+
+ /**
+ * @description Sets the global list of file name patterns to ignore when parsing stack traces
+ * @param {String[]} ignoredOrigins - Array of file name patterns to ignore globally
+ */
+ static setGlobalIgnoredOrigins(ignoredOrigins) {
+ LoggerStackTrace.#globalIgnoredOrigins = ignoredOrigins || [];
+ }
+
+ #ignoredOrigins = [];
+
+ constructor() {
+ // Initialize with global ignored origins
+ this.#ignoredOrigins = [...LoggerStackTrace.#globalIgnoredOrigins];
+ }
+
+ /**
+ * @description Sets the list of file name patterns to ignore when parsing stack traces
+ * @param {String[]} ignoredOrigins - Array of file name patterns to ignore
+ */
+ setIgnoredOrigins(ignoredOrigins) {
+ this.#ignoredOrigins = ignoredOrigins || [];
+ }
+
parse(originStackTraceError) {
if (!originStackTraceError) {
return this;
@@ -158,12 +183,8 @@ export default class LoggerStackTrace {
let originStackTraceParticle;
const parsedStackTraceLines = [];
originStackTraceParticles.forEach(currentStackTraceParticle => {
- if (!originStackTraceParticle && currentStackTraceParticle.fileName?.endsWith('/logger.js')) {
- return;
- }
-
- const ignoredAuraFilenamesRegEx = /aura_prod(?:\.js|debug(?:\.js)?)$/;
- if (!originStackTraceParticle && ignoredAuraFilenamesRegEx.test(currentStackTraceParticle.fileName)) {
+ // Check if this particle should be ignored
+ if (this._shouldIgnoreStackTraceParticle(currentStackTraceParticle, !originStackTraceParticle)) {
return;
}
@@ -178,6 +199,28 @@ export default class LoggerStackTrace {
return { ...originStackTraceParticle, parsedStackTraceString };
}
+ _shouldIgnoreStackTraceParticle(stackTraceParticle, isFirstParticle) {
+ // Always ignore logger.js files
+ if (isFirstParticle && stackTraceParticle.fileName?.endsWith('/logger.js')) {
+ return true;
+ }
+
+ // Check configurable ignored origins
+ for (const ignoredOrigin of this.#ignoredOrigins) {
+ if (stackTraceParticle.fileName?.includes(ignoredOrigin)) {
+ return true;
+ }
+ }
+
+ // Check hardcoded patterns for backward compatibility
+ const ignoredAuraFilenamesRegEx = /aura_prod(?:\.js|debug(?:\.js)?)$/;
+ if (isFirstParticle && ignoredAuraFilenamesRegEx.test(stackTraceParticle.fileName)) {
+ return true;
+ }
+
+ return false;
+ }
+
_cleanStackTraceParticle(stackTraceParticle) {
const lwcModulesFileNamePrefix = 'modules/';
if (stackTraceParticle.fileName?.includes(lwcModulesFileNamePrefix)) {
diff --git a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
index b7f2b57be..a2ecf1878 100644
--- a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
+++ b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls
@@ -102,6 +102,25 @@ private class LoggerParameter_Tests {
System.Assert.areEqual(mockValue, returnedValue);
}
+ @IsTest
+ static void it_should_return_constant_value_for_ignored_javascript_origins() {
+ List mockValue = new List{ 'c/someUtilityComponent', 'c/anotherWrapper' };
+ LoggerParameter__mdt mockParameter = new LoggerParameter__mdt(DeveloperName = 'IgnoredJavaScriptOrigins', Value__c = System.JSON.serialize(mockValue));
+ LoggerParameter.setMock(mockParameter);
+
+ List returnedValue = LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS;
+
+ System.Assert.areEqual(mockValue, returnedValue, 'Returned value does not match expected parameter value');
+ }
+
+ @IsTest
+ static void it_should_return_empty_list_as_default_for_ignored_javascript_origins() {
+ List returnedValue = LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS;
+
+ System.Assert.isNotNull(returnedValue);
+ System.Assert.areEqual(0, returnedValue.size(), 'Default value should be an empty list');
+ }
+
@IsTest
static void it_should_return_constant_value_for_log_batch_purger_default_batch_size() {
Integer mockValue = 1234;
diff --git a/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls
index 1fd6e8f43..ade55c2f8 100644
--- a/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls
+++ b/nebula-logger/core/tests/logger-engine/classes/ComponentLogger_Tests.cls
@@ -43,6 +43,35 @@ private class ComponentLogger_Tests {
Integer returnedOrdinal = componentLoggerSettings.supportedLoggingLevels.get(currentLoggingLevel.name());
System.Assert.areEqual(currentLoggingLevel.ordinal(), returnedOrdinal);
}
+ System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins, 'ignoredJavaScriptOrigins should never be null but empty list by default');
+ }
+
+ @IsTest
+ static void it_should_return_configured_ignored_javascript_origins_in_settings() {
+ List mockIgnoredOrigins = new List{ 'c/someUtilityComponent', 'c/anotherWrapper' };
+ LoggerParameter__mdt mockParameter = new LoggerParameter__mdt(
+ DeveloperName = 'IgnoredJavaScriptOrigins',
+ Value__c = System.JSON.serialize(mockIgnoredOrigins)
+ );
+ LoggerParameter.setMock(mockParameter);
+
+ ComponentLogger.ComponentLoggerSettings componentLoggerSettings = ComponentLogger.getSettings();
+
+ System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins, 'ignoredJavaScriptOrigins wrongly returned as null when a value was configured');
+ System.Assert.areEqual(
+ mockIgnoredOrigins.size(),
+ componentLoggerSettings.ignoredJavaScriptOrigins.size(),
+ 'Size of ignored JavaScript origins list did not match expected size'
+ );
+ System.Assert.areEqual(mockIgnoredOrigins, componentLoggerSettings.ignoredJavaScriptOrigins, 'Ignored JavaScript origins did not match expected values');
+ }
+
+ @IsTest
+ static void it_should_return_empty_ignored_javascript_origins_by_default_in_settings() {
+ ComponentLogger.ComponentLoggerSettings componentLoggerSettings = ComponentLogger.getSettings();
+
+ System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins, 'ignoredJavaScriptOrigins should never be null but empty list by default');
+ System.Assert.areEqual(0, componentLoggerSettings.ignoredJavaScriptOrigins.size(), 'Default should be an empty list');
}
@IsTest