Crash reports handle invalid format strings properly
This commit is contained in:
parent
ccda1eff74
commit
ec6181aaa8
@ -20,193 +20,213 @@ import java.util.*;
|
|||||||
|
|
||||||
public class CrashReports {
|
public class CrashReports {
|
||||||
|
|
||||||
private CrashReports() {
|
private CrashReports() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports");
|
private static final Path CRASH_REPORTS_PATH = Paths.get("crash-reports");
|
||||||
|
|
||||||
private static final Collection<ContextProvider> PROVIDERS = Collections.synchronizedCollection(new ArrayList<>());
|
private static final Collection<ContextProvider> PROVIDERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
private static final Collection<Analyzer> ANALYZERS = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger("crash");
|
private static final Logger LOGGER = LogManager.getLogger("crash");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <em>This method never returns.</em>
|
* <em>This method never returns.</em>
|
||||||
* <p>
|
* <p>
|
||||||
* TODO document
|
* TODO document
|
||||||
*
|
*
|
||||||
* @param throwable
|
* @param throwable
|
||||||
* @param messageFormat
|
* @param messageFormat
|
||||||
* @param args
|
* @param args
|
||||||
*/
|
*/
|
||||||
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
public static void report(Throwable throwable, String messageFormat, Object... args) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
appendContextProviders(output);
|
try {
|
||||||
addSeparator(output);
|
String.format(messageFormat, args);
|
||||||
if (appendAnalyzers(output, throwable, messageFormat, args)) {
|
} catch (IllegalFormatException e) {
|
||||||
addSeparator(output);
|
messageFormat = StringUtil.replaceAll(messageFormat, "%", "%%");
|
||||||
}
|
|
||||||
|
|
||||||
appendMessageFormat(output, messageFormat, args);
|
if (args.length != 0) {
|
||||||
|
messageFormat += "\nArgs:";
|
||||||
|
for (Object arg : args) {
|
||||||
|
try {
|
||||||
|
messageFormat += " \"" + arg.toString() + "\"";
|
||||||
|
} catch (Throwable t) {
|
||||||
|
messageFormat += " exc: \"" + t.getClass().toString() + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args = new Object[0]; // clear args
|
||||||
|
}
|
||||||
|
|
||||||
appendStackTrace(output, throwable);
|
messageFormat += "\nCould not format provided description";
|
||||||
|
}
|
||||||
|
|
||||||
export(output.toString());
|
appendContextProviders(output);
|
||||||
|
addSeparator(output);
|
||||||
|
if (appendAnalyzers(output, throwable, messageFormat, args)) {
|
||||||
|
addSeparator(output);
|
||||||
|
}
|
||||||
|
|
||||||
System.exit(0);
|
appendMessageFormat(output, messageFormat, args);
|
||||||
}
|
|
||||||
|
|
||||||
private static void appendContextProviders(StringBuilder output) {
|
appendStackTrace(output, throwable);
|
||||||
|
|
||||||
// Do a local copy to avoid deadlocks -OLEGSHA
|
export(output.toString());
|
||||||
ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]);
|
|
||||||
|
|
||||||
for (ContextProvider provider : localProvidersCopy) {
|
System.exit(0);
|
||||||
if (provider == null)
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
addSeparator(output);
|
private static void appendContextProviders(StringBuilder output) {
|
||||||
|
|
||||||
try {
|
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||||
Map<String, String> buf = new HashMap<>();
|
ContextProvider[] localProvidersCopy = PROVIDERS.toArray(new ContextProvider[PROVIDERS.size()]);
|
||||||
provider.provideContext(buf);
|
|
||||||
|
|
||||||
if (!buf.isEmpty()) {
|
for (ContextProvider provider : localProvidersCopy) {
|
||||||
output.append("Provider name: ").append(provider.getName()).append("\n");
|
if (provider == null)
|
||||||
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
continue;
|
||||||
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
output.append("\n");
|
|
||||||
|
|
||||||
String providerName;
|
addSeparator(output);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
providerName = provider.getName();
|
Map<String, String> buf = new HashMap<>();
|
||||||
} catch (Throwable t1) {
|
provider.provideContext(buf);
|
||||||
providerName = provider.getClass().getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
output.append(providerName).append(" is broken").append("\n");
|
if (!buf.isEmpty()) {
|
||||||
// ContextProvider is broken
|
output.append("Provider name: ").append(provider.getName()).append("\n");
|
||||||
}
|
for (Map.Entry<String, String> entry : buf.entrySet()) {
|
||||||
}
|
output.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
output.append("\n");
|
||||||
|
|
||||||
private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat,
|
String providerName;
|
||||||
Object[] args) {
|
|
||||||
boolean analyzerResponsesExist = false;
|
|
||||||
|
|
||||||
// Do a local copy to avoid deadlocks -OLEGSHA
|
try {
|
||||||
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
providerName = provider.getName();
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
providerName = provider.getClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
for (Analyzer analyzer : localAnalyzersCopy) {
|
output.append(providerName).append(" is broken").append("\n");
|
||||||
if (analyzer == null)
|
// ContextProvider is broken
|
||||||
continue;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String answer;
|
private static boolean appendAnalyzers(StringBuilder output, Throwable throwable, String messageFormat,
|
||||||
try {
|
Object[] args) {
|
||||||
answer = analyzer.analyze(throwable, messageFormat, args);
|
boolean analyzerResponsesExist = false;
|
||||||
|
|
||||||
if (answer != null && !answer.isEmpty()) {
|
// Do a local copy to avoid deadlocks -OLEGSHA
|
||||||
analyzerResponsesExist = true;
|
Analyzer[] localAnalyzersCopy = ANALYZERS.toArray(new Analyzer[ANALYZERS.size()]);
|
||||||
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
|
||||||
}
|
|
||||||
} catch (Throwable t) {
|
|
||||||
analyzerResponsesExist = true;
|
|
||||||
|
|
||||||
output.append("\n");
|
for (Analyzer analyzer : localAnalyzersCopy) {
|
||||||
|
if (analyzer == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
String analyzerName;
|
String answer;
|
||||||
|
try {
|
||||||
|
answer = analyzer.analyze(throwable, messageFormat, args);
|
||||||
|
|
||||||
try {
|
if (answer != null && !answer.isEmpty()) {
|
||||||
analyzerName = analyzer.getName();
|
analyzerResponsesExist = true;
|
||||||
} catch (Throwable t1) {
|
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
||||||
analyzerName = analyzer.getClass().getName();
|
}
|
||||||
}
|
} catch (Throwable t) {
|
||||||
|
analyzerResponsesExist = true;
|
||||||
|
|
||||||
output.append(analyzerName).append(" is broken").append("\n");
|
output.append("\n");
|
||||||
// Analyzer is broken
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return analyzerResponsesExist;
|
String analyzerName;
|
||||||
}
|
|
||||||
|
|
||||||
private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) {
|
try {
|
||||||
output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n");
|
analyzerName = analyzer.getName();
|
||||||
|
} catch (Throwable t1) {
|
||||||
|
analyzerName = analyzer.getClass().getName();
|
||||||
|
}
|
||||||
|
|
||||||
addSeparator(output);
|
output.append(analyzerName).append(" is broken").append("\n");
|
||||||
}
|
// Analyzer is broken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void appendStackTrace(StringBuilder output, Throwable throwable) {
|
return analyzerResponsesExist;
|
||||||
output.append("Stacktrace: \n");
|
}
|
||||||
|
|
||||||
if (throwable == null) {
|
private static void appendMessageFormat(StringBuilder output, String messageFormat, Object... arg) {
|
||||||
output.append("no Throwable provided").append("\n");
|
output.append("Provided description: \n").append(String.format(messageFormat, arg)).append("\n");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatting to a human-readable string
|
addSeparator(output);
|
||||||
Writer sink = new StringBuilderWriter(output);
|
}
|
||||||
try {
|
|
||||||
throwable.printStackTrace(new PrintWriter(sink));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// PLAK
|
|
||||||
}
|
|
||||||
output.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void export(String report) {
|
private static void appendStackTrace(StringBuilder output, Throwable throwable) {
|
||||||
try {
|
output.append("Stacktrace: \n");
|
||||||
LOGGER.fatal("/n" + report);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// PLAK
|
|
||||||
}
|
|
||||||
|
|
||||||
System.err.println(report);
|
if (throwable == null) {
|
||||||
|
output.append("no Throwable provided").append("\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
generateCrashReportFiles(report);
|
// Formatting to a human-readable string
|
||||||
}
|
Writer sink = new StringBuilderWriter(output);
|
||||||
|
try {
|
||||||
|
throwable.printStackTrace(new PrintWriter(sink));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// PLAK
|
||||||
|
}
|
||||||
|
output.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
private static void generateCrashReportFiles(String output) {
|
private static void export(String report) {
|
||||||
Date date = new Date();
|
try {
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
LOGGER.fatal("/n" + report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// PLAK
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
System.err.println(report);
|
||||||
if (!Files.exists(CRASH_REPORTS_PATH))
|
|
||||||
Files.createDirectory(CRASH_REPORTS_PATH);
|
|
||||||
|
|
||||||
createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log");
|
generateCrashReportFiles(report);
|
||||||
createFileForCrashReport(output,
|
}
|
||||||
CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log");
|
|
||||||
} catch (Throwable t) {
|
|
||||||
// Crash Report not created
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void createFileForCrashReport(String buffer, String filename) {
|
private static void generateCrashReportFiles(String output) {
|
||||||
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) {
|
Date date = new Date();
|
||||||
writer.write(buffer);
|
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
||||||
} catch (IOException ex) {
|
|
||||||
// Crash Report not created
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerProvider(ContextProvider provider) {
|
try {
|
||||||
PROVIDERS.add(provider);
|
if (!Files.exists(CRASH_REPORTS_PATH))
|
||||||
}
|
Files.createDirectory(CRASH_REPORTS_PATH);
|
||||||
|
|
||||||
public static void registerAnalyzer(Analyzer analyzer) {
|
createFileForCrashReport(output, CRASH_REPORTS_PATH.toString() + "/latest.log");
|
||||||
ANALYZERS.add(analyzer);
|
createFileForCrashReport(output,
|
||||||
}
|
CRASH_REPORTS_PATH.toString() + "/crash-" + dateFormat.format(date) + ".log");
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Crash Report not created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void addSeparator(StringBuilder sb) {
|
private static void createFileForCrashReport(String buffer, String filename) {
|
||||||
sb.append(
|
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) {
|
||||||
// 80 chars
|
writer.write(buffer);
|
||||||
"--------------------------------------------------------------------------------").append("\n");
|
} catch (IOException ex) {
|
||||||
}
|
// Crash Report not created
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerProvider(ContextProvider provider) {
|
||||||
|
PROVIDERS.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerAnalyzer(Analyzer analyzer) {
|
||||||
|
ANALYZERS.add(analyzer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addSeparator(StringBuilder sb) {
|
||||||
|
sb.append(
|
||||||
|
// 80 chars
|
||||||
|
"--------------------------------------------------------------------------------").append("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user