Skip to content

Commit 98e54e7

Browse files
authored
Merge pull request #1252 from rainboyan
Various improvements related to stack trace logging and exception display - ErrorsViewStackTracePrinter should use `grailsResourceLocator` first - And `grailsResourceLocator` need to set `searchLocation` to `BuildSettings.BASE_DIR` - Display the relative path of the file associated with the errors - Auto-configure StackTraceFilterer bean on GrailsExceptionResolver - Improve the log messages of exception's stack trace - Fixed StackTracePrinterSpec - Add an additional Spring configuration metadata: `grails.full.stacktrace` - Formatting request parameters and log information, the excluded parameters will be marked [FILTERED] in the log Closes gh-1252
2 parents cea4095 + 08b15d9 commit 98e54e7

File tree

12 files changed

+187
-82
lines changed

12 files changed

+187
-82
lines changed

grace-bootstrap/src/main/groovy/org/grails/exceptions/reporting/DefaultStackTraceFilterer.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2022 the original author or authors.
2+
* Copyright 2011-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,8 +25,9 @@
2525
/**
2626
* Default implementation of StackTraceFilterer.
2727
*
28-
* @since 2.0
2928
* @author Graeme Rocher
29+
* @author Michael Yan
30+
* @since 2.0
3031
*/
3132
public class DefaultStackTraceFilterer implements StackTraceFilterer {
3233

@@ -37,16 +38,18 @@ public class DefaultStackTraceFilterer implements StackTraceFilterer {
3738
private static final String[] DEFAULT_INTERNAL_PACKAGES = new String[] {
3839
"org.codehaus.groovy.runtime.",
3940
"org.codehaus.groovy.reflection.",
41+
"org.codehaus.groovy.vmplugin.",
4042
"org.codehaus.groovy.ast.",
4143
"org.springframework.web.filter",
4244
"org.springframework.boot.actuate",
43-
"org.mortbay.",
4445
"groovy.lang.",
4546
"org.apache.catalina.",
4647
"org.apache.coyote.",
4748
"org.apache.tomcat.",
4849
"net.sf.cglib.proxy.",
4950
"sun.",
51+
"jdk.internal.reflect.",
52+
"java.lang.",
5053
"java.lang.reflect.",
5154
"org.springframework.boot.devtools.",
5255
"org.springsource.loaded.",
@@ -69,28 +72,33 @@ public DefaultStackTraceFilterer(boolean shouldFilter) {
6972
this.packagesToFilter.addAll(Arrays.asList(DEFAULT_INTERNAL_PACKAGES));
7073
}
7174

75+
@Override
7276
public void addInternalPackage(String name) {
7377
if (name == null) {
7478
throw new IllegalArgumentException("Package name cannot be null");
7579
}
7680
this.packagesToFilter.add(name);
7781
}
7882

83+
@Override
7984
public void setCutOffPackage(String cutOffPackage) {
8085
this.cutOffPackage = cutOffPackage;
8186
}
8287

88+
@Override
8389
public Throwable filter(Throwable source, boolean recursive) {
8490
if (recursive) {
8591
Throwable current = source;
8692
while (current != null) {
8793
current = filter(current);
8894
current = current.getCause();
8995
}
96+
return current;
9097
}
9198
return filter(source);
9299
}
93100

101+
@Override
94102
public Throwable filter(Throwable source) {
95103
if (this.shouldFilter) {
96104
StackTraceElement[] trace = source.getStackTrace();
@@ -149,8 +157,14 @@ protected boolean isApplicationClass(String className) {
149157
return true;
150158
}
151159

160+
@Override
152161
public void setShouldFilter(boolean shouldFilter) {
153162
this.shouldFilter = shouldFilter;
154163
}
155164

165+
@Override
166+
public boolean isShouldFilter() {
167+
return this.shouldFilter;
168+
}
169+
156170
}

grace-bootstrap/src/main/groovy/org/grails/exceptions/reporting/StackTraceFilterer.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2022 the original author or authors.
2+
* Copyright 2011-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,8 +18,9 @@
1818
/**
1919
* Improves the output of stack traces produced by exceptions in a Grails application.
2020
*
21-
* @since 2.0
2221
* @author Graeme Rocher
22+
* @author Michaael Yan
23+
* @since 2.0
2324
*/
2425
public interface StackTraceFilterer {
2526

@@ -62,4 +63,9 @@ public interface StackTraceFilterer {
6263
*/
6364
void setShouldFilter(boolean shouldFilter);
6465

66+
/**
67+
* @return boolean Whether to filter stack traces or not
68+
*/
69+
boolean isShouldFilter();
70+
6571
}

grace-core/src/main/groovy/grails/validation/ValidationException.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2009-2022 the original author or authors.
2+
* Copyright 2009-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
* Thrown when validation fails during a .save().
2525
*
2626
* @author Jeff Brown
27+
* @author Michael Yan
2728
* @since 1.2
2829
*/
2930
public class ValidationException extends GrailsException {
@@ -54,8 +55,11 @@ public static String formatErrors(Errors errors) {
5455

5556
public static String formatErrors(Errors errors, String msg) {
5657
StringBuilder b = new StringBuilder();
57-
if (msg != null && msg.length() > 0) {
58-
b.append(msg).append(":\n");
58+
if (msg != null && !msg.isEmpty()) {
59+
b.append(msg);
60+
}
61+
if (errors.hasErrors()) {
62+
b.append(":\n");
5963
}
6064
for (ObjectError error : errors.getAllErrors()) {
6165
b.append("- ").append(error).append("\n");

grace-core/src/main/groovy/org/grails/core/exceptions/DefaultErrorsPrinter.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.grails.exceptions.reporting.StackTracePrinter
3131
*
3232
* @author Graeme Rocher
3333
* @author Marc Palmer
34+
* @author Michael Yan
3435
* @since 2.0
3536
*/
3637
class DefaultErrorsPrinter extends DefaultStackTracePrinter implements CodeSnippetPrinter {
@@ -82,7 +83,7 @@ class DefaultErrorsPrinter extends DefaultStackTracePrinter implements CodeSnipp
8283
}
8384
}
8485

85-
if (!className || !lineNumber) {
86+
if (!className || lineNumber < 0) {
8687
continue
8788
}
8889

grace-core/src/test/groovy/org/grails/exception/reporting/StackTracePrinterSpec.groovy

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ class StackTracePrinterSpec extends Specification {
2929

3030
then:"The formatting is correctly applied"
3131
result != null
32-
result.contains '| 7 | callMe . . . . . . in test.FooController'
32+
result.contains '->> 7 | callMe in test.FooController'
3333
}
3434

35-
@Requires({jvm.isJava8()})
3635
void "Test pretty print nested stack trace"() {
3736
given: "a controller that throws an exception"
3837
final gcl = new GroovyClassLoader()

grace-gradle-plugin/src/main/groovy/org/grails/gradle/plugin/core/GrailsGradlePlugin.groovy

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,6 @@ class GrailsGradlePlugin extends GroovyPlugin {
316316
task.systemProperty Metadata.APPLICATION_VERSION, project.version
317317
task.systemProperty Metadata.APPLICATION_GRAILS_VERSION, grailsVersion
318318
task.systemProperty Environment.KEY, defaultGrailsEnv
319-
task.systemProperty Environment.FULL_STACKTRACE, System.getProperty(Environment.FULL_STACKTRACE) ?: ''
320319
if (task.minHeapSize == null) {
321320
task.minHeapSize = '768m'
322321
}

grace-plugin-controllers/src/main/groovy/org/grails/plugins/web/controllers/ControllersPluginConfiguration.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2022-2023 the original author or authors.
2+
* Copyright 2022-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@
2222
import jakarta.servlet.MultipartConfigElement;
2323
import jakarta.servlet.Servlet;
2424

25+
import org.springframework.beans.BeanUtils;
2526
import org.springframework.beans.factory.ObjectProvider;
2627
import org.springframework.boot.autoconfigure.AutoConfiguration;
2728
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -43,7 +44,10 @@
4344
import grails.config.Config;
4445
import grails.config.Settings;
4546
import grails.core.GrailsApplication;
47+
import grails.util.Environment;
4648

49+
import org.grails.exceptions.reporting.DefaultStackTraceFilterer;
50+
import org.grails.exceptions.reporting.StackTraceFilterer;
4751
import org.grails.web.errors.GrailsExceptionResolver;
4852
import org.grails.web.filters.HiddenHttpMethodFilter;
4953
import org.grails.web.filters.OrderedHiddenHttpMethodFilter;
@@ -81,11 +85,30 @@ public FilterRegistrationBean<GrailsWebRequestFilter> grailsWebRequestFilter(
8185
}
8286

8387
@Bean
84-
public GrailsExceptionResolver exceptionHandler() {
88+
@ConditionalOnMissingBean
89+
public StackTraceFilterer stackTraceFilterer(ObjectProvider<GrailsApplication> grailsApplicationProvider) {
90+
GrailsApplication grailsApplication = grailsApplicationProvider.getIfAvailable();
91+
Config config = grailsApplication.getConfig();
92+
boolean shouldFilter = !config.getProperty(Environment.FULL_STACKTRACE, Boolean.class, Boolean.FALSE);
93+
Class<?> filtererClass = config.getProperty(Settings.SETTING_LOGGING_STACKTRACE_FILTER_CLASS,
94+
Class.class, DefaultStackTraceFilterer.class);
95+
96+
StackTraceFilterer filtererBean = BeanUtils.instantiateClass(filtererClass, StackTraceFilterer.class);
97+
filtererBean.setShouldFilter(shouldFilter);
98+
return filtererBean;
99+
}
100+
101+
@Bean
102+
public GrailsExceptionResolver exceptionHandler(ObjectProvider<GrailsApplication> grailsApplicationProvider,
103+
ObjectProvider<StackTraceFilterer> stackTraceFiltererObjectProvider) {
85104
GrailsExceptionResolver exceptionResolver = new GrailsExceptionResolver();
105+
exceptionResolver.setGrailsApplication(grailsApplicationProvider.getIfAvailable());
106+
exceptionResolver.setStackTraceFilterer(stackTraceFiltererObjectProvider.getIfAvailable());
107+
86108
Properties exceptionMappings = new Properties();
87109
exceptionMappings.put("java.lang.Exception", "/error");
88110
exceptionResolver.setExceptionMappings(exceptionMappings);
111+
89112
return exceptionResolver;
90113
}
91114

grace-plugin-controllers/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
"type": "java.lang.String",
3838
"defaultValue": "\/static\/**"
3939
},
40+
{
41+
"name": "grails.full.stacktrace",
42+
"description": "Whether the display of full stack traces is needed.",
43+
"type": "java.lang.Boolean",
44+
"defaultValue": false
45+
},
4046
{
4147
"name": "grails.exceptionresolver.logRequestParameters",
4248
"description": "Whether to log request parameters in the console.",

grace-plugin-gsp/src/main/groovy/org/grails/plugins/web/GroovyPagesAutoConfiguration.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Map;
2222

2323
import org.springframework.beans.factory.ObjectProvider;
24+
import org.springframework.beans.factory.annotation.Qualifier;
2425
import org.springframework.beans.factory.config.PropertiesFactoryBean;
2526
import org.springframework.boot.autoconfigure.AutoConfiguration;
2627
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -157,7 +158,6 @@ public CachingGrailsConventionGroovyPageLocator groovyPageLocator(GroovyPagesPro
157158
}
158159

159160
@Bean
160-
@ConditionalOnMissingBean
161161
public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesProperties, ObjectProvider<GrailsApplication> grailsApplication) {
162162
Config config = grailsApplication.getIfAvailable().getConfig();
163163
Environment env = Environment.getCurrent();
@@ -168,6 +168,7 @@ public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesPr
168168
(developmentMode && env == Environment.DEVELOPMENT) ? 0L : groovyPagesProperties.getReload().getInterval());
169169

170170
CachingGroovyPageStaticResourceLocator groovyPageStaticResourceLocator = new CachingGroovyPageStaticResourceLocator();
171+
groovyPageStaticResourceLocator.setSearchLocation(BuildSettings.BASE_DIR.getAbsolutePath());
171172

172173
if (enableReload) {
173174
groovyPageStaticResourceLocator.setCacheTimeout(gspCacheTimeout);
@@ -178,7 +179,8 @@ public ResourceLocator grailsResourceLocator(GroovyPagesProperties groovyPagesPr
178179

179180
@Bean
180181
@ConditionalOnMissingBean
181-
public ErrorsViewStackTracePrinter errorsViewStackTracePrinter(ObjectProvider<ResourceLocator> grailsResourceLocator) {
182+
public ErrorsViewStackTracePrinter errorsViewStackTracePrinter(
183+
@Qualifier("grailsResourceLocator") ObjectProvider<ResourceLocator> grailsResourceLocator) {
182184
return new ErrorsViewStackTracePrinter(grailsResourceLocator.getObject());
183185
}
184186

0 commit comments

Comments
 (0)