From 848d990c8dcc43aa5b91fb34cadf61d89b8b6205 Mon Sep 17 00:00:00 2001 From: jmercie Date: Tue, 24 Feb 2026 22:06:24 -0300 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20add=20support=20for=20ignored=20Jav?= =?UTF-8?q?aScript=20origins=20in=20stack=20trace=20parsing=20=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Introduced `LoggerParameter.IgnoredJavaScriptOrigins` to configure JavaScript file patterns to ignore. - Updated `LoggerStackTrace` to utilize ignored origins for cleaner stack traces. - Enhanced `ComponentLogger` and `LoggerService` to manage ignored origins. - Added tests to verify functionality of ignored origins in settings and stack trace parsing. --- nebula-logger/core.package.xml | 1 + .../configuration/classes/LoggerParameter.cls | 16 ++++++ ...meter.IgnoredJavaScriptOrigins.md-meta.xml | 19 +++++++ .../logger-engine/classes/ComponentLogger.cls | 8 +++ .../logger/__tests__/loggerStackTrace.test.js | 41 ++++++++++++++ .../logger-engine/lwc/logger/loggerService.js | 12 ++++ .../lwc/logger/loggerStackTrace.js | 55 +++++++++++++++++-- .../classes/LoggerParameter_Tests.cls | 19 +++++++ .../classes/ComponentLogger_Tests.cls | 26 +++++++++ 9 files changed, 191 insertions(+), 6 deletions(-) create mode 100644 nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredJavaScriptOrigins.md-meta.xml 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..43ddfcca0 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); + } + + @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..2f9a2aaba 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,32 @@ private class ComponentLogger_Tests { Integer returnedOrdinal = componentLoggerSettings.supportedLoggingLevels.get(currentLoggingLevel.name()); System.Assert.areEqual(currentLoggingLevel.ordinal(), returnedOrdinal); } + System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins); + System.Assert.areEqual(LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS, componentLoggerSettings.ignoredJavaScriptOrigins); + } + + @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); + System.Assert.areEqual(mockIgnoredOrigins.size(), componentLoggerSettings.ignoredJavaScriptOrigins.size()); + System.Assert.areEqual(mockIgnoredOrigins, componentLoggerSettings.ignoredJavaScriptOrigins); + } + + @IsTest + static void it_should_return_empty_ignored_javascript_origins_by_default_in_settings() { + ComponentLogger.ComponentLoggerSettings componentLoggerSettings = ComponentLogger.getSettings(); + + System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins); + System.Assert.areEqual(0, componentLoggerSettings.ignoredJavaScriptOrigins.size(), 'Default should be an empty list'); } @IsTest From 343f3a1de7c90a353aaf1361799e8bc23545c822 Mon Sep 17 00:00:00 2001 From: jmercie Date: Tue, 24 Feb 2026 22:21:46 -0300 Subject: [PATCH 2/2] test: add message param to asserts --- .../classes/LoggerParameter_Tests.cls | 2 +- .../classes/ComponentLogger_Tests.cls | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls index 43ddfcca0..a2ecf1878 100644 --- a/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls +++ b/nebula-logger/core/tests/configuration/classes/LoggerParameter_Tests.cls @@ -110,7 +110,7 @@ private class LoggerParameter_Tests { List returnedValue = LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS; - System.Assert.areEqual(mockValue, returnedValue); + System.Assert.areEqual(mockValue, returnedValue, 'Returned value does not match expected parameter value'); } @IsTest 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 2f9a2aaba..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,8 +43,7 @@ private class ComponentLogger_Tests { Integer returnedOrdinal = componentLoggerSettings.supportedLoggingLevels.get(currentLoggingLevel.name()); System.Assert.areEqual(currentLoggingLevel.ordinal(), returnedOrdinal); } - System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins); - System.Assert.areEqual(LoggerParameter.IGNORED_JAVASCRIPT_ORIGINS, componentLoggerSettings.ignoredJavaScriptOrigins); + System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins, 'ignoredJavaScriptOrigins should never be null but empty list by default'); } @IsTest @@ -58,16 +57,20 @@ private class ComponentLogger_Tests { ComponentLogger.ComponentLoggerSettings componentLoggerSettings = ComponentLogger.getSettings(); - System.Assert.isNotNull(componentLoggerSettings.ignoredJavaScriptOrigins); - System.Assert.areEqual(mockIgnoredOrigins.size(), componentLoggerSettings.ignoredJavaScriptOrigins.size()); - System.Assert.areEqual(mockIgnoredOrigins, componentLoggerSettings.ignoredJavaScriptOrigins); + 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); + 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'); }