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..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 @@ -409,6 +409,16 @@ 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); + } + /* // 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