From 64507848d53042aed3d69296ffd360fd034f7c73 Mon Sep 17 00:00:00 2001 From: jmercie Date: Tue, 23 Sep 2025 13:37:52 -0300 Subject: [PATCH 1/2] feat: Add declarative configuration for ignoring Apex classes in stack traces. This allows admins to globally configure Apex classes to be filtered from stack traces without requiring developer code changes, addressing issue #890. --- nebula-logger/core.package.xml | 1 + .../configuration/classes/LoggerParameter.cls | 16 ++++++++++ ...erParameter.IgnoredApexOrigins.md-meta.xml | 17 ++++++++++ .../classes/LoggerStackTrace.cls | 12 ++++++- .../classes/LoggerStackTrace_Tests.cls | 32 +++++++++++++++++++ .../CMDT_LoggerParameter_Tests.cls | 11 +++++++ 6 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredApexOrigins.md-meta.xml diff --git a/nebula-logger/core.package.xml b/nebula-logger/core.package.xml index ad32d80a8..52966865c 100644 --- a/nebula-logger/core.package.xml +++ b/nebula-logger/core.package.xml @@ -735,6 +735,7 @@ LoggerParameter.EnableLoggerSystemMessages LoggerParameter.EnableStackTraceParsing LoggerParameter.EnableTagging + LoggerParameter.IgnoredApexOrigins 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..8908224e4 100644 --- a/nebula-logger/core/main/configuration/classes/LoggerParameter.cls +++ b/nebula-logger/core/main/configuration/classes/LoggerParameter.cls @@ -74,6 +74,22 @@ public class LoggerParameter { private set; } + /** + * @description A list of Apex class names that should be ignored when parsing stack traces. + * Any stack trace lines containing these class names will be removed from the parsed stack trace. + * This is useful for filtering out utility or framework classes from stack traces. + * Controlled by the custom metadata record `LoggerParameter.IgnoredApexOrigins`, or an empty list as the default + */ + public static final List IGNORED_APEX_ORIGINS { + get { + if (IGNORED_APEX_ORIGINS == null) { + IGNORED_APEX_ORIGINS = getStringList('IgnoredApexOrigins', new List()); + } + return IGNORED_APEX_ORIGINS; + } + private set; + } + /** * @description Indicates if Nebula Logger will append its own log entries about the logging system. * Controlled by the custom metadata record `LoggerParameter.EnableLoggerSystemMessages`, or `false` as the default diff --git a/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredApexOrigins.md-meta.xml b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredApexOrigins.md-meta.xml new file mode 100644 index 000000000..f52fbb9a4 --- /dev/null +++ b/nebula-logger/core/main/configuration/customMetadata/LoggerParameter.IgnoredApexOrigins.md-meta.xml @@ -0,0 +1,17 @@ + + + + false + + Comments__c + + + + Description__c + A list of Apex class names that should be ignored when parsing stack traces. Any stack trace lines containing these class names will be removed from the parsed stack trace. This is useful for filtering out utility or framework classes from stack traces. The value should be a JSON array of strings, e.g., ["MyUtilityClass", "AnotherFrameworkClass"]. + + + Value__c + [] + + \ No newline at end of file diff --git a/nebula-logger/core/main/logger-engine/classes/LoggerStackTrace.cls b/nebula-logger/core/main/logger-engine/classes/LoggerStackTrace.cls index bd91f0eed..39206c3fa 100644 --- a/nebula-logger/core/main/logger-engine/classes/LoggerStackTrace.cls +++ b/nebula-logger/core/main/logger-engine/classes/LoggerStackTrace.cls @@ -14,7 +14,6 @@ 'PMD.ApexDoc, PMD.AvoidDeeplyNestedIfStmts, PMD.CognitiveComplexity, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount, PMD.NcssMethodCount, PMD.PropertyNamingConventions, PMD.StdCyclomaticComplexity' ) public without sharing class LoggerStackTrace { - private static final Set IGNORED_APEX_ORIGINS = new Set{ LoggerStackTrace.class.getName() }; private static final String NEW_LINE_DELIMITER = '\n'; private static final System.Pattern INVALID_NAMESPACED_STACK_TRACE_PATTERN { @@ -27,6 +26,17 @@ public without sharing class LoggerStackTrace { set; } + private static Set IGNORED_APEX_ORIGINS { + get { + if (IGNORED_APEX_ORIGINS == null) { + Set ignoredOrigins = new Set{ LoggerStackTrace.class.getName() }; + ignoredOrigins.addAll(LoggerParameter.IGNORED_APEX_ORIGINS); + IGNORED_APEX_ORIGINS = ignoredOrigins; + } + return IGNORED_APEX_ORIGINS; + } + private set; + } @SuppressWarnings('PMD.FieldNamingConventions') public enum SourceLanguage { Apex, diff --git a/nebula-logger/core/tests/logger-engine/classes/LoggerStackTrace_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/LoggerStackTrace_Tests.cls index be064c16a..36757aa54 100644 --- a/nebula-logger/core/tests/logger-engine/classes/LoggerStackTrace_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/LoggerStackTrace_Tests.cls @@ -265,6 +265,38 @@ private class LoggerStackTrace_Tests { System.Assert.isTrue(stackTrace.ParsedStackTraceString.contains(externalEntryPointStackTrace)); } + @IsTest + static void it_should_ignore_stack_trace_line_when_matching_class_name_has_been_declaratively_ignored() { + String ignoredClassName = 'SomeIgnoredUtilityClass'; + String ignoredStackTraceLine = 'Class.' + ignoredClassName + '.someMethod: line 99999, column 1'; + String nonIgnoredTopLevelClassName = 'TheNextClassNameInTheStack'; + Integer nonIgnoredTopLevelClassLineNumber = 987; + String nonIgnoredMethodName = 'doSomething'; + String expectedOriginLocation = nonIgnoredTopLevelClassName + '.' + nonIgnoredMethodName; + String relevantStackTraceLines = + 'Class.' + + expectedOriginLocation + + ': line ' + + nonIgnoredTopLevelClassLineNumber + + ', column 1' + + '\nAnonymousBlock: line 1, column 1'; + String mockStackTrace = ignoredStackTraceLine + '\n' + relevantStackTraceLines; + + LoggerConfigurationSelector.useMocks(); + LoggerParameter__mdt ignoredOriginsParameter = new LoggerParameter__mdt(DeveloperName = 'IgnoredApexOrigins', Value__c = '["' + ignoredClassName + '"]'); + LoggerConfigurationSelector.mockLoggerParameters.put(ignoredOriginsParameter.DeveloperName, ignoredOriginsParameter); + + LoggerStackTrace stackTrace = new LoggerStackTrace(mockStackTrace); + + System.Assert.areEqual(LoggerStackTrace.SourceLanguage.Apex, stackTrace.Language); + System.Assert.areEqual(expectedOriginLocation, stackTrace.Location); + System.Assert.areEqual(relevantStackTraceLines, stackTrace.ParsedStackTraceString); + System.Assert.areEqual(nonIgnoredTopLevelClassName, stackTrace.Source.ApiName); + System.Assert.areEqual(nonIgnoredMethodName, stackTrace.Source.ActionName); + System.Assert.areEqual(nonIgnoredTopLevelClassLineNumber, stackTrace.Source.LineNumber); + System.Assert.areEqual(LoggerStackTrace.SourceMetadataType.ApexClass, stackTrace.Source.MetadataType); + } + private class DebugStringExample { private System.Exception constructorStackTraceGenerator; private System.Exception methodStackTraceGenerator; diff --git a/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls b/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls index 7c3506081..338f358da 100644 --- a/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls +++ b/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls @@ -409,6 +409,17 @@ private class CMDT_LoggerParameter_Tests { System.Assert.areEqual('false', bundledRecord.Value__c); } + @IsTest + static void it_has_correct_record_for_ignored_apex_origins() { + LoggerParameter__mdt bundledRecord = LoggerParameter__mdt.getInstance('IgnoredApexOrigins'); + + System.Assert.isNotNull(bundledRecord, 'Record is missing'); + System.Assert.isNull(bundledRecord.Comments__c, 'Comments should be null/blank'); + System.Assert.isNotNull(bundledRecord.Description__c, 'Description is missing'); + System.Assert.areEqual('Ignored Apex Origins', bundledRecord.Label); + System.Assert.areEqual('[]', bundledRecord.Value__c); + } + /* // LoggerParameter__mdt checks // The field LoggerParameter__mdt.Description__c is a long textarea field, so it can't be marked as required - but every record From 062cf78b26e779efeee573ea99f16e95871865f2 Mon Sep 17 00:00:00 2001 From: jmercie Date: Tue, 23 Sep 2025 16:13:04 -0300 Subject: [PATCH 2/2] test: remove conflicting assert, that would fail as soon, as users populate a value for the mdt record. --- .../CMDT_LoggerParameter_Tests.cls | 1 - 1 file changed, 1 deletion(-) diff --git a/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls b/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls index 338f358da..1b26169a9 100644 --- a/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls +++ b/nebula-logger/extra-tests/bundled-custom-metadata-type-records/CMDT_LoggerParameter_Tests.cls @@ -417,7 +417,6 @@ private class CMDT_LoggerParameter_Tests { System.Assert.isNull(bundledRecord.Comments__c, 'Comments should be null/blank'); System.Assert.isNotNull(bundledRecord.Description__c, 'Description is missing'); System.Assert.areEqual('Ignored Apex Origins', bundledRecord.Label); - System.Assert.areEqual('[]', bundledRecord.Value__c); } /*