ErrorLogHelper { @NonNull public static Exception getModelExceptionFromThrowable(@NonNull Throwable t) { Exception topException = null; Exception parentException = null; List<Throwable> causeChain = new LinkedList<>(); for (Throwable cause = t; cause != null; cause = cause.getCause()) { causeChain.add(cause); } if (causeChain.size() > CAUSE_LIMIT) { AppCenterLog.warn(Crashes.LOG_TAG, "Crash causes truncated from " + causeChain.size() + " to " + CAUSE_LIMIT + " causes."); causeChain.subList(CAUSE_LIMIT_HALF, causeChain.size() - CAUSE_LIMIT_HALF).clear(); } for (Throwable cause : causeChain) { Exception exception = new Exception(); exception.setType(cause.getClass().getName()); exception.setMessage(cause.getMessage()); exception.setFrames(getModelFramesFromStackTrace(cause)); if (topException == null) { topException = exception; } else { parentException.setInnerExceptions(Collections.singletonList(exception)); } parentException = exception; } return topException; }  @NonNull static ManagedErrorLog createErrorLog(@NonNull Context context, @NonNull final java.lang.Thread thread, @NonNull final Throwable throwable, @NonNull final Map<java.lang.Thread, StackTraceElement[]> allStackTraces, final long initializeTimestamp); @NonNull static ManagedErrorLog createErrorLog(@NonNull Context context, @NonNull final java.lang.Thread thread, @NonNull final Exception exception, @NonNull final Map<java.lang.Thread, StackTraceElement[]> allStackTraces, final long initializeTimestamp, boolean fatal); @NonNull static synchronized File getErrorStorageDirectory(); @NonNull static synchronized File getNewMinidumpDirectory(); @NonNull static synchronized File getNewMinidumpSubfolder(); @NonNull static synchronized File getNewMinidumpSubfolderWithContextData(Context context); @NonNull static synchronized File getPendingMinidumpDirectory(); @NonNull static File[] getStoredErrorLogFiles(); @NonNull static File[] getNewMinidumpFiles(); @Nullable static Device getStoredDeviceInfo(File logFolder); static void removeStaleMinidumpSubfolders(); static void removeMinidumpFolder(); @Nullable static File getLastErrorLogFile(); @Nullable static File getStoredThrowableFile(@NonNull UUID id); static void removeStoredThrowableFile(@NonNull UUID id); static void removeStoredErrorLogFile(@NonNull UUID id); @NonNull static ErrorReport getErrorReportFromErrorLog(@NonNull ManagedErrorLog log, String stackTrace); @NonNull static Exception getModelExceptionFromThrowable(@NonNull Throwable t); static Map<String, String> validateProperties(Map<String, String> properties, String logType); static void cleanPendingMinidumps(); @NonNull static UUID parseLogFolderUuid(File logFolder); @VisibleForTesting static void clearStaticState(); static final String DEVICE_INFO_FILE; static final String ERROR_LOG_FILE_EXTENSION; static final String THROWABLE_FILE_EXTENSION; @VisibleForTesting
    static final int FRAME_LIMIT; static final int MAX_PROPERTY_ITEM_LENGTH; static final String MINIDUMP_FILE_EXTENSION; }
@Test public void truncateCauses() { RuntimeException e = new RuntimeException(); for (int i = 0; i < 32; i++) { e = new RuntimeException(Integer.valueOf(i).toString(), e); } int depth = 1; Exception model = ErrorLogHelper.getModelExceptionFromThrowable(e); while (model.getInnerExceptions() != null && (model = model.getInnerExceptions().get(0)) != null) { depth++; } assertEquals(ErrorLogHelper.CAUSE_LIMIT, depth); }