Code Review
- Renamed CrashReportGenerator.makeCrashReport to .crash for convenience - Split .crash into subroutines - Documented Analyzer and ContextProvider - Both are now supposed to have names In Title Case - CRG.export now assumes PrintStream does not fail and assumes Log can fail
This commit is contained in:
parent
ddf48c0587
commit
044c690d09
@ -38,7 +38,7 @@ public class ProgressiaClientMain {
|
|||||||
long[] ssdss = new long[1 << 30];
|
long[] ssdss = new long[1 << 30];
|
||||||
} catch (Throwable t)
|
} catch (Throwable t)
|
||||||
{
|
{
|
||||||
CrashReportGenerator.makeCrashReport(t, "u %s stupid", "vry");
|
CrashReportGenerator.crash(t, "u %s stupid", "vry");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProgressiaLauncher.launch(args, new ClientProxy());
|
ProgressiaLauncher.launch(args, new ClientProxy());
|
||||||
|
@ -1,7 +1,32 @@
|
|||||||
package ru.windcorp.progressia.common.util.crash;
|
package ru.windcorp.progressia.common.util.crash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A crash report utility that performs analysis of a problem during crash
|
||||||
|
* report generation and presents its conclusion to the user via the crash report.
|
||||||
|
* Unlike {@link ContextProvider}s, Analyzers are provided with the reported problem
|
||||||
|
* details.
|
||||||
|
* @see ContextProvider
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
public interface Analyzer {
|
public interface Analyzer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a human-readable string describing this analyzer's conclusion
|
||||||
|
* on the presented problem, or returns {@code null} if no conclusion
|
||||||
|
* could be made.
|
||||||
|
* @param throwable The reported throwable (may be {@code null})
|
||||||
|
* @param messageFormat A {@linkplain java.util.Formatter#syntax format string} of a
|
||||||
|
* human-readable description of the problem
|
||||||
|
* @param args The arguments for the format string
|
||||||
|
* @return a conclusion or {@code null}
|
||||||
|
*/
|
||||||
String analyze(Throwable throwable, String messageFormat, Object... args);
|
String analyze(Throwable throwable, String messageFormat, Object... args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this analyzer's human-readable name.
|
||||||
|
* It should be A String In Title Case With Spaces.
|
||||||
|
* @return this analyzer's name
|
||||||
|
*/
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,30 @@ package ru.windcorp.progressia.common.util.crash;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A crash report utility that gathers information about game and system state
|
||||||
|
* when a crash occurs and presents it to the user via the crash report.
|
||||||
|
* ContextProviders are not aware of the nature of the problem, unlike {@link Analyzer}s.
|
||||||
|
* @see Analyzer
|
||||||
|
* @author serega404
|
||||||
|
*/
|
||||||
public interface ContextProvider {
|
public interface ContextProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides human-readable description of the state of the game and the system.
|
||||||
|
* This information is {@link Map#put(Object, Object) put} into the provided map
|
||||||
|
* as key-value pairs. Keys are the characteristic being described, such as "OS Name",
|
||||||
|
* and should be Strings In Title Case With Spaces.
|
||||||
|
* If this provider cannot provide any information at this moment, the map is not
|
||||||
|
* modified.
|
||||||
|
* @param output the map to append output to
|
||||||
|
*/
|
||||||
void provideContext(Map<String, String> output);
|
void provideContext(Map<String, String> output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this provider's human-readable name.
|
||||||
|
* It should be A String In Title Case With Spaces.
|
||||||
|
* @return this provider's name
|
||||||
|
*/
|
||||||
String getName();
|
String getName();
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,12 @@ package ru.windcorp.progressia.common.util.crash;
|
|||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.apache.logging.log4j.core.util.StringBuilderWriter;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.Writer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -17,21 +18,43 @@ import java.util.*;
|
|||||||
|
|
||||||
public class CrashReportGenerator {
|
public class CrashReportGenerator {
|
||||||
|
|
||||||
private CrashReportGenerator() {
|
private CrashReportGenerator() {}
|
||||||
}
|
|
||||||
|
|
||||||
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 = new ArrayList<>();
|
private static final Collection<ContextProvider> PROVIDERS =
|
||||||
|
Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
private static final Collection<Analyzer> ANALYZERS = 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");
|
||||||
|
|
||||||
public static void makeCrashReport(Throwable throwable, String messageFormat, Object... args) {
|
/**
|
||||||
|
* <em>This method never returns.</em>
|
||||||
|
* <p>
|
||||||
|
* TODO document
|
||||||
|
* @param throwable
|
||||||
|
* @param messageFormat
|
||||||
|
* @param args
|
||||||
|
*/
|
||||||
|
public static void crash(Throwable throwable, String messageFormat, Object... args) {
|
||||||
StringBuilder output = new StringBuilder();
|
StringBuilder output = new StringBuilder();
|
||||||
|
|
||||||
|
appendContextProviders(output);
|
||||||
|
addSeparator(output);
|
||||||
|
if (appendAnalyzers(output, throwable, messageFormat, args)) {
|
||||||
|
addSeparator(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStackTrace(output, throwable);
|
||||||
|
|
||||||
|
export(output.toString());
|
||||||
|
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void appendContextProviders(StringBuilder output) {
|
||||||
for (ContextProvider provider : PROVIDERS) {
|
for (ContextProvider provider : PROVIDERS) {
|
||||||
if (provider != null) {
|
if (provider != null) {
|
||||||
Map<String, String> buf = new HashMap<>();
|
Map<String, String> buf = new HashMap<>();
|
||||||
@ -55,10 +78,13 @@ public class CrashReportGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
addSeparator(output);
|
|
||||||
|
private static boolean appendAnalyzers(
|
||||||
boolean analyzerResponseExist = false;
|
StringBuilder output,
|
||||||
|
Throwable throwable, String messageFormat, Object[] args
|
||||||
|
) {
|
||||||
|
boolean analyzerResponsesExist = false;
|
||||||
for (Analyzer analyzer : ANALYZERS) {
|
for (Analyzer analyzer : ANALYZERS) {
|
||||||
if (analyzer != null) {
|
if (analyzer != null) {
|
||||||
|
|
||||||
@ -67,12 +93,12 @@ public class CrashReportGenerator {
|
|||||||
answer = analyzer.analyze(throwable, messageFormat, args);
|
answer = analyzer.analyze(throwable, messageFormat, args);
|
||||||
|
|
||||||
if (answer != null && !answer.isEmpty()) {
|
if (answer != null && !answer.isEmpty()) {
|
||||||
analyzerResponseExist = true;
|
analyzerResponsesExist = true;
|
||||||
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
output.append(analyzer.getName()).append(": ").append(answer).append("\n");
|
||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
try {
|
try {
|
||||||
analyzerResponseExist = true;
|
analyzerResponsesExist = true;
|
||||||
output.append(analyzer.getName()).append(" is broken").append("\n");
|
output.append(analyzer.getName()).append(" is broken").append("\n");
|
||||||
} catch (Throwable th) {
|
} catch (Throwable th) {
|
||||||
// You stupid
|
// You stupid
|
||||||
@ -81,38 +107,40 @@ public class CrashReportGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return analyzerResponsesExist;
|
||||||
|
}
|
||||||
|
|
||||||
if (analyzerResponseExist) addSeparator(output);
|
private static void appendStackTrace(StringBuilder output, Throwable throwable) {
|
||||||
|
|
||||||
// Formatting to a human-readable string
|
|
||||||
StringWriter sink = new StringWriter();
|
|
||||||
if (throwable != null) {
|
|
||||||
try {
|
|
||||||
throwable.printStackTrace(new PrintWriter(sink));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// PLAK
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
sink.append("Null");
|
|
||||||
}
|
|
||||||
|
|
||||||
output.append("Stacktrace: \n");
|
output.append("Stacktrace: \n");
|
||||||
output.append(sink.toString()).append("\n");
|
|
||||||
|
if (throwable == null) {
|
||||||
LOGGER.fatal("/n" + output.toString());
|
output.append("no Throwable provided").append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatting to a human-readable string
|
||||||
|
Writer sink = new StringBuilderWriter(output);
|
||||||
try {
|
try {
|
||||||
System.err.println(output.toString());
|
throwable.printStackTrace(new PrintWriter(sink));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// PLAK
|
// PLAK
|
||||||
}
|
}
|
||||||
|
output.append("\n");
|
||||||
generateCrashReportFiles(output.toString());
|
}
|
||||||
|
|
||||||
System.exit(0);
|
private static void export(String report) {
|
||||||
|
try {
|
||||||
|
LOGGER.fatal("/n" + report);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// PLAK
|
||||||
|
}
|
||||||
|
|
||||||
|
System.err.println(report);
|
||||||
|
|
||||||
|
generateCrashReportFiles(report);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void generateCrashReportFiles(String output) {
|
private static void generateCrashReportFiles(String output) {
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
|
||||||
|
|
||||||
@ -121,7 +149,6 @@ public class CrashReportGenerator {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Files.createDirectory(CRASH_REPORTS_PATH);
|
Files.createDirectory(CRASH_REPORTS_PATH);
|
||||||
;
|
|
||||||
pathExist = true;
|
pathExist = true;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Crash Report not created
|
// Crash Report not created
|
||||||
@ -135,8 +162,13 @@ public class CrashReportGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createFileForCrashReport(String buffer, String filename) {
|
private static void createFileForCrashReport(String buffer, String filename) {
|
||||||
try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(filename), StandardCharsets.UTF_8)) {
|
try (
|
||||||
|
BufferedWriter writer = Files.newBufferedWriter(
|
||||||
|
Paths.get(filename),
|
||||||
|
StandardCharsets.UTF_8
|
||||||
|
)
|
||||||
|
) {
|
||||||
writer.write(buffer);
|
writer.write(buffer);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
// Crash Report not created
|
// Crash Report not created
|
||||||
@ -152,6 +184,9 @@ public class CrashReportGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void addSeparator(StringBuilder sb) {
|
private static void addSeparator(StringBuilder sb) {
|
||||||
sb.append("-------------------------------------------------").append("\n");
|
sb.append(
|
||||||
|
// 80 chars
|
||||||
|
"--------------------------------------------------------------------------------"
|
||||||
|
).append("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,12 @@ public class OutOfMemoryAnalyzer implements Analyzer {
|
|||||||
@Override
|
@Override
|
||||||
public String analyze(Throwable throwable, String messageFormat, Object... args) {
|
public String analyze(Throwable throwable, String messageFormat, Object... args) {
|
||||||
if (throwable instanceof OutOfMemoryError)
|
if (throwable instanceof OutOfMemoryError)
|
||||||
return "Try add memory for the JVM";
|
return "Try to add memory to the JVM";
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.getClass().getSimpleName();
|
return "Out Of Memory Analyzer";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,13 +8,13 @@ public class OSContextProvider implements ContextProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void provideContext(Map<String, String> output) {
|
public void provideContext(Map<String, String> output) {
|
||||||
output.put("Name OS", System.getProperty("os.name"));
|
output.put("OS Name", System.getProperty("os.name"));
|
||||||
output.put("Version OS", System.getProperty("os.version"));
|
output.put("OS Version", System.getProperty("os.version"));
|
||||||
output.put("Architecture OS", System.getProperty("os.arch"));
|
output.put("OS Architecture", System.getProperty("os.arch"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.getClass().getSimpleName();
|
return "OS Context Provider";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user