package xapi.util;
import xapi.log.X_Log;
import xapi.util.api.ErrorHandler;
import java.io.PrintStream;
import java.util.Arrays;
public class X_Debug {
private X_Debug() {}
public static AssertionError recommendAssertions() {
// Use a single debug String in production, which recommends -ea compiles for far more details
// in assertion errors. This can allow you to unit test for AssertionError whenever "something that shouldn't happen" occurs,
// without sending along large or sensitive debugging strings to production clients.
return new AssertionError("Recompile with -ea assertions enabled for a better error message.");
}
public static class DebugStream extends PrintStream {
private final PrintStream orig;
private final int depth;
public DebugStream(PrintStream orig, int ignoreDepth) {
super(orig);
this.depth = ignoreDepth;
this.orig = orig;
}
@Override
public void println() {
super.println();
debugCaller();
};
private void debugCaller() {
RuntimeException e = new RuntimeException();
e.fillInStackTrace();
StackTraceElement[] traces = e.getStackTrace();
int index = depth, end = Math.min(depth+10, traces.length);
orig.print("\n\t\t @ ");
for(;index < end; index++ ) {
orig.print(traces[index]+": ");
}
if (X_Runtime.isJava()) {
flush(orig);
}
}
private void flush(PrintStream orig2) {
orig.flush();
}
public PrintStream append(CharSequence str) {
// GWT doesn't have an append method, so we hack it here
print(str.toString());
return this;
}
@Override
public void print(String s) {
super.print(s);
debugCaller();
}
}
public static void debug(Throwable e) {
// TODO: give an injectable service to handle debug messages
// This would be good for threadlocal processes,
// so the object inspecting the throwable will have context for the
// exception
while (e != null) {
e.printStackTrace();
if (e == e.getCause()) return;
e = e.getCause();
}
}
public static boolean isBenchmark() {
return X_Runtime.isDebug();
}
public static void inspect(String qualifiedBinaryName) {
X_Log.error(qualifiedBinaryName);
if (X_Runtime.isDebug()) {
StackTraceElement[] trace = new RuntimeException().getStackTrace();
X_Log.error(Arrays.asList(trace).toString().replaceAll(", ", "\n "));
}
}
@SuppressWarnings("rawtypes")
public static ErrorHandler defaultHandler() {
return DefaultHandler.handler;
}
public static void maybeRethrow(Throwable e) {
if (X_Runtime.isDebug())
debug(e);
Throwable unwrapped = X_Util.unwrap(e);
if (
// NEVER eat these! Getting interrupted while sleeping is no big deal,
// but if you are running, unless you are doing something mission critical,
// an interruption needs to bubble up so threads can be shut down.
unwrapped instanceof InterruptedException) {
// avoid new wrapper instances;
// wrap will rethrow Error, or return a RuntimeException
throw X_Util.rethrow(e);
}
}
public static RuntimeException rethrow(Throwable e) {
debug(e);
throw X_Util.rethrow(e);
}
public static void traceSystemErr() {
traceSystemErr(3);
}
public static void traceSystemErr(int ignoreDepth) {
final PrintStream orig = System.err;
// Avoid cyclic calls, but don't store references...
if (orig instanceof DebugStream)
return;
System.setErr(new DebugStream(orig, ignoreDepth));
System.err.println("Tracing system.err");
}
public static void traceSystemOut() {
traceSystemOut(3);
}
/**
*
* @param ignoreDepth
*/
public static void traceSystemOut(int ignoreDepth) {
final PrintStream orig = System.out;
// Avoid cyclic calls, but don't store references...
if (orig instanceof DebugStream)
return;
System.setOut(new DebugStream(orig, ignoreDepth));
System.out.println("Tracing system.out");
}
}
class DefaultHandler implements ErrorHandler<Throwable> {
static final DefaultHandler handler = new DefaultHandler();
private DefaultHandler() {
}
@Override
public void onError(Throwable e) {
X_Log.error(e);
}
};