-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
/
InstallUncaughtExceptionHandler.java
121 lines (108 loc) · 5.72 KB
/
InstallUncaughtExceptionHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package hudson.init.impl;
import hudson.init.Initializer;
import jenkins.model.Jenkins;
import jenkins.telemetry.impl.java11.MissingClassTelemetry;
import org.kohsuke.MetaInfServices;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.kohsuke.stapler.WebApp;
import org.kohsuke.stapler.compression.CompressionFilter;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.EOFException;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Deals with exceptions that get thrown all the way up to the Stapler rendering layer.
*/
public class InstallUncaughtExceptionHandler {
private static final Logger LOGGER = Logger.getLogger(InstallUncaughtExceptionHandler.class.getName());
@Initializer
public static void init(final Jenkins j) throws IOException {
CompressionFilter.setUncaughtExceptionHandler(j.servletContext, (e, context, req, rsp) -> handleException(j, e, req, rsp, 500));
try {
Thread.setDefaultUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler());
LOGGER.log(Level.FINE, "Successfully installed a global UncaughtExceptionHandler.");
}
catch (SecurityException ex) {
LOGGER.log(Level.SEVERE,
"Failed to set the default UncaughtExceptionHandler. " +
"If any threads die due to unhandled coding errors then there will be no logging of this information. " +
"The lack of this diagnostic information will make it harder to track down issues which will reduce the supportability of Jenkins. " +
"It is highly recommended that you consult the documentation that comes with you servlet container on how to allow the " +
"`setDefaultUncaughtExceptionHandler` permission and enable it.", ex);
}
}
private static void handleException(Jenkins j, Throwable e, HttpServletRequest req, HttpServletResponse rsp, int code) throws IOException, ServletException {
if (rsp.isCommitted()) {
LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, null, e);
return;
}
String id = UUID.randomUUID().toString();
LOGGER.log(isEOFException(e) ? Level.FINE : Level.WARNING, "Caught unhandled exception with ID " + id, e);
req.setAttribute("jenkins.exception.id", id);
req.setAttribute("javax.servlet.error.exception",e);
rsp.setStatus(code);
try {
// If we have an exception, let's see if it's related with missing classes on Java 11. We reach
// here with a ClassNotFoundException in an action, for example. Setting the report here is the only
// way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass
MissingClassTelemetry.reportExceptionInside(e);
WebApp.get(j.servletContext).getSomeStapler().invoke(req, rsp, j, "/oops");
} catch (ServletException | IOException x) {
if (!Stapler.isSocketException(x)) {
throw x;
}
}
}
@Restricted(NoExternalUse.class)
@MetaInfServices
public static class ErrorCustomizer implements HttpResponses.ErrorCustomizer {
@CheckForNull
@Override
public HttpResponses.HttpResponseException handleError(int code, Throwable cause) {
if (Jenkins.getInstanceOrNull() == null) {
return null;
}
return new HttpResponses.HttpResponseException(cause) {
@Override
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
handleException(Jenkins.get(), cause, req, rsp, code);
}
};
}
}
private static boolean isEOFException(Throwable e) {
if (e == null) {
return false;
} else if (e instanceof EOFException) {
return true;
} else {
return isEOFException(e.getCause());
}
}
/** An UncaughtExceptionHandler that just logs the exception */
private static class DefaultUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable ex) {
// if this was an OutOfMemoryError then all bets about logging are off - but in the absence of anything else...
LOGGER.log(Level.SEVERE,
"A thread (" + t.getName() + '/' + t.getId()
+ ") died unexpectedly due to an uncaught exception, this may leave your Jenkins in a bad way and is usually indicative of a bug in the code.",
ex);
// If we have an exception, let's see if it's related with missing classes on Java 11. We reach
// here with a ClassNotFoundException in an action, for example. Setting the report here is the only
// way to catch the missing classes when the plugin uses Thread.currentThread().getContextClassLoader().loadClass
MissingClassTelemetry.reportExceptionInside(ex);
}
}
private InstallUncaughtExceptionHandler() {}
}