codeconsole commented on code in PR #15666:
URL: https://github.com/apache/grails-core/pull/15666#discussion_r3611351096


##########
grails-core/src/main/groovy/grails/util/GrailsUtil.java:
##########
@@ -36,11 +41,51 @@ public class GrailsUtil {
 
     private static final Log LOG = LogFactory.getLog(GrailsUtil.class);
     private static final boolean LOG_DEPRECATED = 
Boolean.valueOf(System.getProperty("grails.log.deprecated", 
String.valueOf(Environment.isDevelopmentMode())));
-    private static final StackTraceFilterer stackFilterer = new 
DefaultStackTraceFilterer();
+
+    /**
+     * Default filterer used before {@link 
#initializeStackFilterer(GrailsApplication)} runs (CLI,
+     * tests that don't boot a context, plain {@code main()} usage). Preserves 
the pre-PR behaviour
+     * of a single hardcoded {@link DefaultStackTraceFilterer} instance for 
the JVM lifetime when no
+     * application is wired.
+     */
+    private static final StackTraceFilterer FALLBACK_FILTERER = new 
DefaultStackTraceFilterer();
+
+    /**
+     * Active filterer for {@link #printSanitizedStackTrace}, {@link 
#sanitizeRootCause} and
+     * {@link #deepSanitize}. Starts as {@link #FALLBACK_FILTERER} and is 
replaced with a
+     * config-driven instance when {@link 
#initializeStackFilterer(GrailsApplication)} runs during
+     * Grails bootstrap. Volatile so the bootstrap-time write publishes safely 
to the request
+     * threads that read it later.
+     */
+    private static volatile StackTraceFilterer stackFilterer = 
FALLBACK_FILTERER;
 
     private GrailsUtil() {
     }
 
+    /**
+     * Installs a {@link StackTraceFilterer} resolved from the given 
application's config, replacing
+     * the default fallback. Reads {@link 
Settings#SETTING_LOGGING_STACKTRACE_FILTER_CLASS} for the
+     * filterer class and propagates {@link 
Settings#SETTING_LOG_FULL_STACKTRACE_ON_FILTER} to
+     * instances of {@link DefaultStackTraceFilterer}. Called by {@code 
GrailsExceptionResolver}
+     * during Spring bean wiring (which is the same point the resolver 
consults these keys for its
+     * own filterer), so request-time callers of the static {@code 
sanitize}/{@code deepSanitize}
+     * methods see the configured instance.
+     *
+     * <p>No-ops when {@code application} is null. Safe to call more than once 
— the last successful
+     * invocation wins.
+     *
+     * @since 7.1.5
+     */
+    public static void initializeStackFilterer(GrailsApplication application) {

Review Comment:
   Went with a variant of your second option: 
GrailsUtil.initializeStackFilterer now takes the already-resolved 
StackTraceFilterer instance rather than an Environment, so GrailsUtil has zero 
dependency on Spring/Grails config types at all -- 
GrailsBootstrapRegistryInitializer does the resolution and hands over a ready 
instance. One thing worth flagging: environment.getProperty(key, Class.class, 
default) won't actually do the String->Class conversion -- neither Spring's 
DefaultConversionService nor Boot's ApplicationConversionService register a 
String-to-Class converter (checked both jars), so that call throws 
ConverterNotFoundException for a real class name. The resolver in 
GrailsBootstrapRegistryInitializer reads the property as a String and resolves 
it via ClassUtils.forName instead.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to