source: josm/trunk/src/org/openstreetmap/josm/tools/bugreport/BugReport.java@ 12781

Last change on this file since 12781 was 12770, checked in by bastiK, 7 years ago

see #15229 - remove GUI references from BugReport and BugReportQueue

signature of BugReport#getReportText changed without deprecation
(probably not used by any external plugin)

  • Property svn:eol-style set to native
File size: 7.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools.bugreport;
3
4import java.io.PrintWriter;
5import java.io.Serializable;
6import java.io.StringWriter;
7import java.util.concurrent.CopyOnWriteArrayList;
8import java.util.function.Predicate;
9
10/**
11 * This class contains utility methods to create and handle a bug report.
12 * <p>
13 * It allows you to configure the format and request to send the bug report.
14 * <p>
15 * It also contains the main entry point for all components to use the bug report system: Call {@link #intercept(Throwable)} to start handling an
16 * exception.
17 * <h1> Handling Exceptions </h1>
18 * In your code, you should add try...catch blocks for any runtime exceptions that might happen. It is fine to catch throwable there.
19 * <p>
20 * You should then add some debug information there. This can be the OSM ids that caused the error, information on the data you were working on
21 * or other local variables. Make sure that no exceptions may occur while computing the values. It is best to send plain local variables to
22 * put(...). If you need to do computations, put them into a lambda expression. Then simply throw the throwable you got from the bug report.
23 * The global exception handler will do the rest.
24 * <pre>
25 * int id = ...;
26 * String tag = "...";
27 * try {
28 * ... your code ...
29 * } catch (RuntimeException t) {
30 * throw BugReport.intercept(t).put("id", id).put("tag", () -&gt; x.getTag());
31 * }
32 * </pre>
33 *
34 * Instead of re-throwing, you can call {@link ReportedException#warn()}. This will display a warning to the user and allow it to either report
35 * the exception or ignore it.
36 *
37 * @author Michael Zangl
38 * @since 10285
39 */
40public final class BugReport implements Serializable {
41 private static final long serialVersionUID = 1L;
42
43 private boolean includeStatusReport = true;
44 private boolean includeData = true;
45 private boolean includeAllStackTraces;
46 private final ReportedException exception;
47 private final CopyOnWriteArrayList<BugReportListener> listeners = new CopyOnWriteArrayList<>();
48
49 /**
50 * Create a new bug report
51 * @param e The {@link ReportedException} to use. No more data should be added after creating the report.
52 */
53 public BugReport(ReportedException e) {
54 this.exception = e;
55 includeAllStackTraces = e.mayHaveConcurrentSource();
56 }
57
58 /**
59 * Determines if this report should include a system status report
60 * @return <code>true</code> to include it.
61 * @since 10597
62 */
63 public boolean isIncludeStatusReport() {
64 return includeStatusReport;
65 }
66
67 /**
68 * Set if this report should include a system status report
69 * @param includeStatusReport if the status report should be included
70 * @since 10585
71 */
72 public void setIncludeStatusReport(boolean includeStatusReport) {
73 this.includeStatusReport = includeStatusReport;
74 fireChange();
75 }
76
77 /**
78 * Determines if this report should include the data that was traced.
79 * @return <code>true</code> to include it.
80 * @since 10597
81 */
82 public boolean isIncludeData() {
83 return includeData;
84 }
85
86 /**
87 * Set if this report should include the data that was traced.
88 * @param includeData if data should be included
89 * @since 10585
90 */
91 public void setIncludeData(boolean includeData) {
92 this.includeData = includeData;
93 fireChange();
94 }
95
96 /**
97 * Determines if this report should include the stack traces for all other threads.
98 * @return <code>true</code> to include it.
99 * @since 10597
100 */
101 public boolean isIncludeAllStackTraces() {
102 return includeAllStackTraces;
103 }
104
105 /**
106 * Sets if this report should include the stack traces for all other threads.
107 * @param includeAllStackTraces if all stack traces should be included
108 * @since 10585
109 */
110 public void setIncludeAllStackTraces(boolean includeAllStackTraces) {
111 this.includeAllStackTraces = includeAllStackTraces;
112 fireChange();
113 }
114
115 /**
116 * Gets the full string that should be send as error report.
117 * @param header header text for the error report
118 * @return The string.
119 * @since 10585
120 */
121 public String getReportText(String header) {
122 StringWriter stringWriter = new StringWriter();
123 PrintWriter out = new PrintWriter(stringWriter);
124 if (isIncludeStatusReport()) {
125 try {
126 out.println(header);
127 } catch (RuntimeException e) { // NOPMD
128 out.println("Could not generate status report: " + e.getMessage());
129 }
130 }
131 if (isIncludeData()) {
132 exception.printReportDataTo(out);
133 }
134 exception.printReportStackTo(out);
135 if (isIncludeAllStackTraces()) {
136 exception.printReportThreadsTo(out);
137 }
138 return stringWriter.toString().replaceAll("\r", "");
139 }
140
141 /**
142 * Add a new change listener.
143 * @param listener The listener
144 * @since 10585
145 */
146 public void addChangeListener(BugReportListener listener) {
147 listeners.add(listener);
148 }
149
150 /**
151 * Remove a change listener.
152 * @param listener The listener
153 * @since 10585
154 */
155 public void removeChangeListener(BugReportListener listener) {
156 listeners.remove(listener);
157 }
158
159 private void fireChange() {
160 listeners.stream().forEach(l -> l.bugReportChanged(this));
161 }
162
163 /**
164 * This should be called whenever you want to add more information to a given exception.
165 * @param t The throwable that was thrown.
166 * @return A {@link ReportedException} to which you can add additional information.
167 */
168 public static ReportedException intercept(Throwable t) {
169 ReportedException e;
170 if (t instanceof ReportedException) {
171 e = (ReportedException) t;
172 } else {
173 e = new ReportedException(t);
174 }
175 e.startSection(getCallingMethod(2));
176 return e;
177 }
178
179 /**
180 * Find the method that called us.
181 *
182 * @param offset
183 * How many methods to look back in the stack trace. 1 gives the method calling this method, 0 gives you getCallingMethod().
184 * @return The method name.
185 */
186 public static String getCallingMethod(int offset) {
187 StackTraceElement found = getCallingMethod(offset + 1, BugReport.class.getName(), "getCallingMethod"::equals);
188 if (found != null) {
189 return found.getClassName().replaceFirst(".*\\.", "") + '#' + found.getMethodName();
190 } else {
191 return "?";
192 }
193 }
194
195 /**
196 * Find the method that called the given method on the current stack trace.
197 * @param offset
198 * How many methods to look back in the stack trace.
199 * 1 gives the method calling this method, 0 gives you the method with the given name..
200 * @param className The name of the class to search for
201 * @param methodName The name of the method to search for
202 * @return The class and method name or null if it is unknown.
203 */
204 public static StackTraceElement getCallingMethod(int offset, String className, Predicate<String> methodName) {
205 StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
206 for (int i = 0; i < stackTrace.length - offset; i++) {
207 StackTraceElement element = stackTrace[i];
208 if (className.equals(element.getClassName()) && methodName.test(element.getMethodName())) {
209 return stackTrace[i + offset];
210 }
211 }
212 return null;
213 }
214
215 /**
216 * A listener that listens to changes to this report.
217 * @author Michael Zangl
218 * @since 10585
219 */
220 @FunctionalInterface
221 public interface BugReportListener {
222 /**
223 * Called whenever this bug report was changed, e.g. the data to be included in it.
224 * @param report The report that was changed.
225 */
226 void bugReportChanged(BugReport report);
227 }
228}
Note: See TracBrowser for help on using the repository browser.