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


##########
grails-core/src/main/groovy/grails/util/GrailsUtil.java:
##########
@@ -154,7 +167,84 @@ public static Throwable sanitizeRootCause(Throwable t) {
      * @return The root cause exception instances, with stack trace modified 
to filter out grails runtime classes
      */
     public static Throwable deepSanitize(Throwable t) {
-        return stackFilterer.filter(t, true);
+        return resolveStackFilterer().filter(t, true);
+    }
+
+    /**
+     * Returns the {@link StackTraceFilterer} used by this class, lazily 
initialised from the
+     * Grails application config when one is discoverable. Honours
+     * {@link Settings#SETTING_LOGGING_STACKTRACE_FILTER_CLASS} (the filterer 
class — same key
+     * the exception resolver consults) and propagates
+     * {@link Settings#SETTING_LOG_FULL_STACKTRACE_ON_FILTER} to instances of
+     * {@link DefaultStackTraceFilterer}.
+     *
+     * <p>While no {@link GrailsApplication} is available (early-init paths, 
plain {@code main}
+     * usage, tests that don't wire one up) a fresh {@link 
DefaultStackTraceFilterer} is returned
+     * and <em>not</em> cached — so once the application context boots, the 
next call resolves
+     * the configured filterer for real. After that the value is cached for 
the lifetime of the
+     * JVM, matching the historical behaviour of the previous {@code static 
final} field.
+     */
+    private static StackTraceFilterer resolveStackFilterer() {
+        StackTraceFilterer cached = stackFilterer;
+        if (cached != null) {
+            return cached;
+        }
+        GrailsApplication application = findApplicationQuietly();

Review Comment:
   Right, that was a real regression of the prior invariant. Fixed in cdffb8f3 
with a `static final FALLBACK_FILTERER` sentinel: the volatile `stackFilterer` 
field is initialised to the sentinel so CLI/test/`main()` paths see one shared 
instance for the JVM lifetime, matching the pre-PR `static final` shape. The 
bootstrap hook (C4 thread) then overwrites it once during Spring bean wiring.



##########
grails-core/src/main/groovy/grails/util/GrailsUtil.java:
##########
@@ -154,7 +167,84 @@ public static Throwable sanitizeRootCause(Throwable t) {
      * @return The root cause exception instances, with stack trace modified 
to filter out grails runtime classes
      */
     public static Throwable deepSanitize(Throwable t) {
-        return stackFilterer.filter(t, true);
+        return resolveStackFilterer().filter(t, true);
+    }
+
+    /**
+     * Returns the {@link StackTraceFilterer} used by this class, lazily 
initialised from the
+     * Grails application config when one is discoverable. Honours
+     * {@link Settings#SETTING_LOGGING_STACKTRACE_FILTER_CLASS} (the filterer 
class — same key
+     * the exception resolver consults) and propagates
+     * {@link Settings#SETTING_LOG_FULL_STACKTRACE_ON_FILTER} to instances of
+     * {@link DefaultStackTraceFilterer}.
+     *
+     * <p>While no {@link GrailsApplication} is available (early-init paths, 
plain {@code main}
+     * usage, tests that don't wire one up) a fresh {@link 
DefaultStackTraceFilterer} is returned
+     * and <em>not</em> cached — so once the application context boots, the 
next call resolves
+     * the configured filterer for real. After that the value is cached for 
the lifetime of the
+     * JVM, matching the historical behaviour of the previous {@code static 
final} field.
+     */
+    private static StackTraceFilterer resolveStackFilterer() {
+        StackTraceFilterer cached = stackFilterer;
+        if (cached != null) {
+            return cached;
+        }
+        GrailsApplication application = findApplicationQuietly();
+        if (application == null) {
+            // No application discoverable yet — return an uncached default. A 
later call,
+            // once the context is up, will run through the 
configured-resolution branch
+            // and populate the cache.
+            return new DefaultStackTraceFilterer();

Review Comment:
   Good point — switched to the bootstrap-hook approach in cdffb8f3. 
`GrailsExceptionResolver.setGrailsApplication` now calls a new public 
`GrailsUtil.initializeStackFilterer(application)` immediately after 
`createStackFilterer()`, mirroring how the resolver consumes the same two 
config keys. The `static final FALLBACK_FILTERER` sentinel is the field's 
initial value so CLI/test/`main()` paths work unchanged, but in any Grails web 
app the configured filterer is pinned during bean wiring — no per-access 
initialization, no `Holders` lookup at runtime, hot path is one volatile read.



-- 
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