NaTo1000's picture
download
raw
7.32 kB
package com.termux.shared.crash;
import android.content.Context;
import androidx.annotation.NonNull;
import com.termux.shared.file.FileUtils;
import com.termux.shared.logger.Logger;
import com.termux.shared.markdown.MarkdownUtils;
import com.termux.shared.errors.Error;
import com.termux.shared.android.AndroidUtils;
import java.nio.charset.Charset;
/**
* Catches uncaught exceptions and logs them.
*/
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private final Context mContext;
private final CrashHandlerClient mCrashHandlerClient;
private final Thread.UncaughtExceptionHandler mDefaultUEH;
private final boolean mIsDefaultHandler;
private static final String LOG_TAG = "CrashUtils";
private CrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient,
boolean isDefaultHandler) {
mContext = context;
mCrashHandlerClient = crashHandlerClient;
mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
mIsDefaultHandler = isDefaultHandler;
}
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
Logger.logInfo(LOG_TAG, "uncaughtException() for " + thread + ": " + throwable.getMessage());
logCrash(thread, throwable);
// Don't stop the app if not on the main thread
if (mIsDefaultHandler)
mDefaultUEH.uncaughtException(thread, throwable);
}
/**
* Set default uncaught crash handler for the app to {@link CrashHandler}.
*/
public static void setDefaultCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
if (!(Thread.getDefaultUncaughtExceptionHandler() instanceof CrashHandler)) {
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(context, crashHandlerClient, true));
}
}
/**
* Set uncaught crash handler of current non-main thread to {@link CrashHandler}.
*/
public static void setCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
Thread.currentThread().setUncaughtExceptionHandler(new CrashHandler(context, crashHandlerClient, false));
}
/**
* Get {@link CrashHandler} instance that can be set as uncaught crash handler of a non-main thread.
*/
public static CrashHandler getCrashHandler(@NonNull final Context context, @NonNull final CrashHandlerClient crashHandlerClient) {
return new CrashHandler(context, crashHandlerClient, false);
}
/**
* Log a crash in the crash log file at path returned by {@link CrashHandlerClient#getCrashLogFilePath(Context)}.
*
* @param context The {@link Context} for operations.
* @param crashHandlerClient The {@link CrashHandlerClient} implementation.
* @param thread The {@link Thread} in which the crash happened.
* @param throwable The {@link Throwable} thrown for the crash.
*/
public static void logCrash(@NonNull Context context,
@NonNull CrashHandlerClient crashHandlerClient,
@NonNull Thread thread, @NonNull Throwable throwable) {
Logger.logInfo(LOG_TAG, "logCrash() for " + thread + ": " + throwable.getMessage());
new CrashHandler(context, crashHandlerClient, false).logCrash(thread, throwable);
}
public void logCrash(@NonNull Thread thread, @NonNull Throwable throwable) {
if (!mCrashHandlerClient.onPreLogCrash(mContext, thread, throwable)) {
logCrashToFile(mContext, mCrashHandlerClient, thread, throwable);
mCrashHandlerClient.onPostLogCrash(mContext, thread, throwable);
}
}
public void logCrashToFile(@NonNull Context context,
@NonNull CrashHandlerClient crashHandlerClient,
@NonNull Thread thread, @NonNull Throwable throwable) {
StringBuilder reportString = new StringBuilder();
reportString.append("## Crash Details\n");
reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Thread", thread.toString(), "-"));
reportString.append("\n").append(MarkdownUtils.getSingleLineMarkdownStringEntry("Crash Timestamp", AndroidUtils.getCurrentMilliSecondUTCTimeStamp(), "-"));
reportString.append("\n\n").append(MarkdownUtils.getMultiLineMarkdownStringEntry("Crash Message", throwable.getMessage(), "-"));
reportString.append("\n\n").append(Logger.getStackTracesMarkdownString("Stacktrace", Logger.getStackTracesStringArray(throwable)));
String appInfoMarkdownString = crashHandlerClient.getAppInfoMarkdownString(context);
if (appInfoMarkdownString != null && !appInfoMarkdownString.isEmpty())
reportString.append("\n\n").append(appInfoMarkdownString);
reportString.append("\n\n").append(AndroidUtils.getDeviceInfoMarkdownString(context));
// Log report string to logcat
Logger.logError(reportString.toString());
// Write report string to crash log file
Error error = FileUtils.writeTextToFile("crash log", crashHandlerClient.getCrashLogFilePath(context),
Charset.defaultCharset(), reportString.toString(), false);
if (error != null) {
Logger.logErrorExtended(LOG_TAG, error.toString());
}
}
public interface CrashHandlerClient {
/**
* Called before {@link #logCrashToFile(Context, CrashHandlerClient, Thread, Throwable)} is called.
*
* @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
* @param thread The {@link Thread} in which the crash happened.
* @param throwable The {@link Throwable} thrown for the crash.
* @return Should return {@code true} if crash has been handled and should not be logged,
* otherwise {@code false}.
*/
boolean onPreLogCrash(Context context, Thread thread, Throwable throwable);
/**
* Called after {@link #logCrashToFile(Context, CrashHandlerClient, Thread, Throwable)} is called.
*
* @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
* @param thread The {@link Thread} in which the crash happened.
* @param throwable The {@link Throwable} thrown for the crash.
*/
void onPostLogCrash(Context context, Thread thread, Throwable throwable);
/**
* Get crash log file path.
*
* @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
* @return Should return the crash log file path.
*/
@NonNull
String getCrashLogFilePath(Context context);
/**
* Get app info markdown string to add to crash log.
*
* @param context The {@link Context} passed to {@link CrashHandler#CrashHandler(Context, CrashHandlerClient, boolean)}.
* @return Should return app info markdown string.
*/
String getAppInfoMarkdownString(Context context);
}
}

Xet Storage Details

Size:
7.32 kB
·
Xet hash:
bb9a2247c126924cb5052ffda74725746d0882590b5ec0740204b7fe07d53652

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.