16
16
17
17
package org .springframework .web .util ;
18
18
19
+ import java .util .HashSet ;
19
20
import java .util .Locale ;
20
21
import java .util .Set ;
21
22
24
25
25
26
import org .springframework .core .NestedExceptionUtils ;
26
27
import org .springframework .util .Assert ;
28
+ import org .springframework .util .ClassUtils ;
27
29
28
30
/**
29
- * Utility methods to assist with identifying and logging exceptions that indicate
30
- * the client has gone away. Such exceptions fill logs with unnecessary stack
31
- * traces. The utility methods help to log a single line message at DEBUG level,
32
- * and a full stacktrace at TRACE level.
31
+ * Utility methods to assist with identifying and logging exceptions that
32
+ * indicate the server response connection is lost, for example because the
33
+ * client has gone away. This class helps to identify such exceptions and
34
+ * minimize logging to a single line at DEBUG level, while making the full
35
+ * error stacktrace at TRACE level.
33
36
*
34
37
* @author Rossen Stoyanchev
35
38
* @since 6.1
36
39
*/
37
40
public class DisconnectedClientHelper {
38
41
39
- // Look for server response connection issues (aborted), not onward connections
40
- // to other servers (500 errors).
41
-
42
42
private static final Set <String > EXCEPTION_PHRASES =
43
43
Set .of ("broken pipe" , "connection reset by peer" );
44
44
45
45
private static final Set <String > EXCEPTION_TYPE_NAMES =
46
46
Set .of ("AbortedException" , "ClientAbortException" ,
47
47
"EOFException" , "EofException" , "AsyncRequestNotUsableException" );
48
48
49
+ private static final Set <Class <?>> CLIENT_EXCEPTION_TYPES = new HashSet <>(2 );
50
+
51
+ static {
52
+ try {
53
+ ClassLoader classLoader = DisconnectedClientHelper .class .getClassLoader ();
54
+ CLIENT_EXCEPTION_TYPES .add (ClassUtils .forName (
55
+ "org.springframework.web.client.RestClientException" , classLoader ));
56
+ CLIENT_EXCEPTION_TYPES .add (ClassUtils .forName (
57
+ "org.springframework.web.reactive.function.client.WebClientException" , classLoader ));
58
+ }
59
+ catch (ClassNotFoundException ex ) {
60
+ // ignore
61
+ }
62
+ }
63
+
64
+
49
65
private final Log logger ;
50
66
51
67
@@ -85,6 +101,22 @@ else if (logger.isDebugEnabled()) {
85
101
* </ul>
86
102
*/
87
103
public static boolean isClientDisconnectedException (Throwable ex ) {
104
+ Throwable currentEx = ex ;
105
+ Throwable lastEx = null ;
106
+ while (currentEx != null && currentEx != lastEx ) {
107
+ // Ignore onward connection issues to other servers (500 error)
108
+ for (Class <?> exceptionType : CLIENT_EXCEPTION_TYPES ) {
109
+ if (exceptionType .isInstance (currentEx )) {
110
+ return false ;
111
+ }
112
+ }
113
+ if (EXCEPTION_TYPE_NAMES .contains (currentEx .getClass ().getSimpleName ())) {
114
+ return true ;
115
+ }
116
+ lastEx = currentEx ;
117
+ currentEx = currentEx .getCause ();
118
+ }
119
+
88
120
String message = NestedExceptionUtils .getMostSpecificCause (ex ).getMessage ();
89
121
if (message != null ) {
90
122
String text = message .toLowerCase (Locale .ROOT );
@@ -94,7 +126,8 @@ public static boolean isClientDisconnectedException(Throwable ex) {
94
126
}
95
127
}
96
128
}
97
- return EXCEPTION_TYPE_NAMES .contains (ex .getClass ().getSimpleName ());
129
+
130
+ return false ;
98
131
}
99
132
100
133
}
0 commit comments