mirror of https://github.com/M66B/FairEmail.git
Updated Bugsnag
This commit is contained in:
parent
b5a61240b0
commit
90bcc0a965
|
@ -104,7 +104,7 @@ internal class AppDataCollector(
|
|||
return null
|
||||
}
|
||||
|
||||
val nowMs = System.currentTimeMillis()
|
||||
val nowMs = SystemClock.elapsedRealtime()
|
||||
var durationMs: Long = 0
|
||||
|
||||
val sessionStartTimeMs: Long = sessionTracker.lastEnteredForegroundMs
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package com.bugsnag.android;
|
||||
|
||||
import com.bugsnag.android.internal.DateUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.bugsnag.android
|
||||
|
||||
import com.bugsnag.android.internal.DateUtils
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
|
|||
final ClientObservable clientObservable;
|
||||
PluginClient pluginClient;
|
||||
|
||||
final Notifier notifier = new Notifier();
|
||||
final Notifier notifier;
|
||||
|
||||
@Nullable
|
||||
final LastRunInfo lastRunInfo;
|
||||
|
@ -117,6 +117,8 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
|
|||
ContextModule contextModule = new ContextModule(androidContext);
|
||||
appContext = contextModule.getCtx();
|
||||
|
||||
notifier = configuration.getNotifier();
|
||||
|
||||
connectivity = new ConnectivityCompat(appContext, new Function2<Boolean, String, Unit>() {
|
||||
@Override
|
||||
public Unit invoke(Boolean hasConnection, String networkState) {
|
||||
|
@ -233,7 +235,8 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
|
|||
DeliveryDelegate deliveryDelegate,
|
||||
LastRunInfoStore lastRunInfoStore,
|
||||
LaunchCrashTracker launchCrashTracker,
|
||||
ExceptionHandler exceptionHandler
|
||||
ExceptionHandler exceptionHandler,
|
||||
Notifier notifier
|
||||
) {
|
||||
this.immutableConfig = immutableConfig;
|
||||
this.metadataState = metadataState;
|
||||
|
@ -255,6 +258,7 @@ public class Client implements MetadataAware, CallbackAware, UserAware {
|
|||
this.launchCrashTracker = launchCrashTracker;
|
||||
this.lastRunInfo = null;
|
||||
this.exceptionHandler = exceptionHandler;
|
||||
this.notifier = notifier;
|
||||
}
|
||||
|
||||
void registerLifecycleCallbacks() {
|
||||
|
|
|
@ -21,7 +21,8 @@ internal class ClientObservable : BaseObservable() {
|
|||
conf.buildUuid,
|
||||
conf.releaseStage,
|
||||
lastRunInfoPath,
|
||||
consecutiveLaunchCrashes
|
||||
consecutiveLaunchCrashes,
|
||||
conf.sendThreads
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ internal class ConfigInternal(var apiKey: String) : CallbackAware, MetadataAware
|
|||
var projectPackages: Set<String> = emptySet()
|
||||
var persistenceDirectory: File? = null
|
||||
|
||||
val notifier: Notifier = Notifier()
|
||||
|
||||
protected val plugins = HashSet<Plugin>()
|
||||
|
||||
override fun addOnError(onError: OnErrorCallback) = callbackState.addOnError(onError)
|
||||
|
@ -79,7 +81,7 @@ internal class ConfigInternal(var apiKey: String) : CallbackAware, MetadataAware
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_MAX_BREADCRUMBS = 25
|
||||
private const val DEFAULT_MAX_BREADCRUMBS = 50
|
||||
private const val DEFAULT_MAX_PERSISTED_SESSIONS = 128
|
||||
private const val DEFAULT_MAX_PERSISTED_EVENTS = 32
|
||||
private const val DEFAULT_LAUNCH_CRASH_THRESHOLD_MS: Long = 5000
|
||||
|
|
|
@ -513,7 +513,7 @@ public class Configuration implements CallbackAware, MetadataAware, UserAware {
|
|||
* Sets the maximum number of breadcrumbs which will be stored. Once the threshold is reached,
|
||||
* the oldest breadcrumbs will be deleted.
|
||||
*
|
||||
* By default, 25 breadcrumbs are stored: this can be amended up to a maximum of 100.
|
||||
* By default, 50 breadcrumbs are stored: this can be amended up to a maximum of 100.
|
||||
*/
|
||||
public int getMaxBreadcrumbs() {
|
||||
return impl.getMaxBreadcrumbs();
|
||||
|
@ -523,7 +523,7 @@ public class Configuration implements CallbackAware, MetadataAware, UserAware {
|
|||
* Sets the maximum number of breadcrumbs which will be stored. Once the threshold is reached,
|
||||
* the oldest breadcrumbs will be deleted.
|
||||
*
|
||||
* By default, 25 breadcrumbs are stored: this can be amended up to a maximum of 100.
|
||||
* By default, 50 breadcrumbs are stored: this can be amended up to a maximum of 100.
|
||||
*/
|
||||
public void setMaxBreadcrumbs(int maxBreadcrumbs) {
|
||||
if (maxBreadcrumbs >= MIN_BREADCRUMBS && maxBreadcrumbs <= MAX_BREADCRUMBS) {
|
||||
|
@ -981,4 +981,8 @@ public class Configuration implements CallbackAware, MetadataAware, UserAware {
|
|||
Set<Plugin> getPlugins() {
|
||||
return impl.getPlugins();
|
||||
}
|
||||
|
||||
Notifier getNotifier() {
|
||||
return impl.getNotifier();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
package com.bugsnag.android;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
class DateUtils {
|
||||
// SimpleDateFormat isn't thread safe, cache one instance per thread as needed.
|
||||
private static final ThreadLocal<DateFormat> iso8601Holder = new ThreadLocal<DateFormat>() {
|
||||
@NonNull
|
||||
@Override
|
||||
protected DateFormat initialValue() {
|
||||
TimeZone tz = TimeZone.getTimeZone("UTC");
|
||||
DateFormat iso8601 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
|
||||
iso8601.setTimeZone(tz);
|
||||
return iso8601;
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
static String toIso8601(@NonNull Date date) {
|
||||
DateFormat dateFormat = iso8601Holder.get();
|
||||
if (dateFormat == null) {
|
||||
throw new IllegalStateException("Unable to find valid dateformatter");
|
||||
}
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static Date fromIso8601(@NonNull String date) {
|
||||
try {
|
||||
return iso8601Holder.get().parse(date);
|
||||
} catch (ParseException exc) {
|
||||
throw new IllegalArgumentException("Failed to parse timestamp", exc);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.bugsnag.android
|
||||
|
||||
import com.bugsnag.android.internal.DateUtils
|
||||
import java.io.OutputStream
|
||||
import java.security.DigestOutputStream
|
||||
import java.security.MessageDigest
|
||||
|
|
|
@ -430,10 +430,36 @@ public class NativeInterface {
|
|||
getClient().setAutoDetectAnrs(autoDetectAnrs);
|
||||
}
|
||||
|
||||
public static void startSession() {
|
||||
getClient().startSession();
|
||||
}
|
||||
|
||||
public static void pauseSession() {
|
||||
getClient().pauseSession();
|
||||
}
|
||||
|
||||
public static boolean resumeSession() {
|
||||
return getClient().resumeSession();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Session getCurrentSession() {
|
||||
return getClient().sessionTracker.getCurrentSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the launch period as complete
|
||||
*/
|
||||
public static void markLaunchCompleted() {
|
||||
getClient().markLaunchCompleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last run info object
|
||||
*/
|
||||
@Nullable
|
||||
public static LastRunInfo getLastRunInfo() {
|
||||
return getClient().getLastRunInfo();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import java.io.IOException
|
|||
*/
|
||||
class Notifier @JvmOverloads constructor(
|
||||
var name: String = "Android Bugsnag Notifier",
|
||||
var version: String = "5.12.0",
|
||||
var version: String = "5.14.0",
|
||||
var url: String = "https://bugsnag.com"
|
||||
) : JsonStream.Streamable {
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.bugsnag.android
|
||||
|
||||
import com.bugsnag.android.internal.DateUtils
|
||||
import java.io.IOException
|
||||
import java.lang.reflect.Array
|
||||
import java.util.Date
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package com.bugsnag.android;
|
||||
|
||||
import com.bugsnag.android.internal.DateUtils;
|
||||
import com.bugsnag.android.internal.ImmutableConfig;
|
||||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
@ -319,11 +322,11 @@ class SessionTracker extends BaseObservable {
|
|||
}
|
||||
|
||||
void onActivityStarted(String activityName) {
|
||||
updateForegroundTracker(activityName, true, System.currentTimeMillis());
|
||||
updateForegroundTracker(activityName, true, SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
void onActivityStopped(String activityName) {
|
||||
updateForegroundTracker(activityName, false, System.currentTimeMillis());
|
||||
updateForegroundTracker(activityName, false, SystemClock.elapsedRealtime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -349,7 +352,7 @@ class SessionTracker extends BaseObservable {
|
|||
|
||||
if (noActivityRunningForMs >= timeoutMs
|
||||
&& configuration.getAutoTrackSessions()) {
|
||||
startNewSession(new Date(nowMs), client.getUser(), true);
|
||||
startNewSession(new Date(), client.getUser(), true);
|
||||
}
|
||||
}
|
||||
foregroundActivities.add(activityName);
|
||||
|
|
|
@ -9,7 +9,8 @@ sealed class StateEvent { // JvmField allows direct field access optimizations
|
|||
@JvmField val buildUuid: String?,
|
||||
@JvmField val releaseStage: String?,
|
||||
@JvmField val lastRunInfoPath: String,
|
||||
@JvmField val consecutiveLaunchCrashes: Int
|
||||
@JvmField val consecutiveLaunchCrashes: Int,
|
||||
@JvmField val sendThreads: ThreadSendPolicy
|
||||
) : StateEvent()
|
||||
|
||||
object DeliverPending : StateEvent()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.bugsnag.android;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
@ -19,9 +20,10 @@ public class Thread implements JsonStream.Streamable {
|
|||
@NonNull String name,
|
||||
@NonNull ThreadType type,
|
||||
boolean errorReportingThread,
|
||||
@NonNull Thread.State state,
|
||||
@NonNull Stacktrace stacktrace,
|
||||
@NonNull Logger logger) {
|
||||
this.impl = new ThreadInternal(id, name, type, errorReportingThread, stacktrace);
|
||||
this.impl = new ThreadInternal(id, name, type, errorReportingThread, state, stacktrace);
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
@ -81,6 +83,25 @@ public class Thread implements JsonStream.Streamable {
|
|||
return impl.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state of thread (from {@link java.lang.Thread})
|
||||
*/
|
||||
public void setState(@NonNull Thread.State threadState) {
|
||||
if (threadState != null) {
|
||||
impl.setState(threadState);
|
||||
} else {
|
||||
logNull("state");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the thread (from {@link java.lang.Thread})
|
||||
*/
|
||||
@NonNull
|
||||
public Thread.State getState() {
|
||||
return impl.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the thread was the thread that caused the event
|
||||
*/
|
||||
|
@ -111,4 +132,79 @@ public class Thread implements JsonStream.Streamable {
|
|||
public void toStream(@NonNull JsonStream stream) throws IOException {
|
||||
impl.toStream(stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of a reported {@link Thread}. These states correspond directly to
|
||||
* {@link java.lang.Thread.State}, except for {@code UNKNOWN} which indicates that
|
||||
* a state could not be captured or mapped.
|
||||
*/
|
||||
public enum State {
|
||||
NEW("NEW"),
|
||||
BLOCKED("BLOCKED"),
|
||||
RUNNABLE("RUNNABLE"),
|
||||
TERMINATED("TERMINATED"),
|
||||
TIMED_WAITING("TIMED_WAITING"),
|
||||
WAITING("WAITING"),
|
||||
UNKNOWN("UNKNOWN");
|
||||
|
||||
private final String descriptor;
|
||||
|
||||
State(String descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static State forThread(@NonNull java.lang.Thread thread) {
|
||||
java.lang.Thread.State state = thread.getState();
|
||||
return getState(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the {@code State} for a given {@link #getDescriptor() descriptor} code. Unlike
|
||||
* {@link #valueOf(String) valueOf}, this method will return {@link #UNKNOWN} is no
|
||||
* matching {@code State} constant can be found.
|
||||
*
|
||||
* @param descriptor a consistent descriptor of the state constant to lookup
|
||||
* @return the requested {@link State} or {@link #UNKNOWN}
|
||||
*/
|
||||
@NonNull
|
||||
public static State byDescriptor(@Nullable String descriptor) {
|
||||
if (descriptor == null) {
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
for (State state : values()) {
|
||||
if (state.getDescriptor().equals(descriptor)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static State getState(java.lang.Thread.State state) {
|
||||
switch (state) {
|
||||
case NEW:
|
||||
return NEW;
|
||||
case BLOCKED:
|
||||
return BLOCKED;
|
||||
case RUNNABLE:
|
||||
return RUNNABLE;
|
||||
case TERMINATED:
|
||||
return TERMINATED;
|
||||
case TIMED_WAITING:
|
||||
return TIMED_WAITING;
|
||||
case WAITING:
|
||||
return WAITING;
|
||||
default:
|
||||
return UNKNOWN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ class ThreadInternal internal constructor(
|
|||
var name: String,
|
||||
var type: ThreadType,
|
||||
val isErrorReportingThread: Boolean,
|
||||
var state: Thread.State,
|
||||
stacktrace: Stacktrace
|
||||
) : JsonStream.Streamable {
|
||||
|
||||
|
@ -18,6 +19,7 @@ class ThreadInternal internal constructor(
|
|||
writer.name("id").value(id)
|
||||
writer.name("name").value(name)
|
||||
writer.name("type").value(type.desc)
|
||||
writer.name("state").value(state.descriptor)
|
||||
|
||||
writer.name("stacktrace")
|
||||
writer.beginArray()
|
||||
|
|
|
@ -67,7 +67,7 @@ internal class ThreadState @Suppress("LongParameterList") @JvmOverloads construc
|
|||
if (trace != null) {
|
||||
val stacktrace = Stacktrace(trace, projectPackages, logger)
|
||||
val errorThread = thread.id == currentThreadId
|
||||
Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, stacktrace, logger)
|
||||
Thread(thread.id, thread.name, ThreadType.ANDROID, errorThread, Thread.State.forThread(thread), stacktrace, logger)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package com.bugsnag.android.internal
|
||||
|
||||
import java.text.DateFormat
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.TimeZone
|
||||
|
||||
object DateUtils {
|
||||
// SimpleDateFormat isn't thread safe, cache one instance per thread as needed.
|
||||
private val iso8601Holder = object : ThreadLocal<DateFormat>() {
|
||||
override fun initialValue(): DateFormat {
|
||||
return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply {
|
||||
timeZone = TimeZone.getTimeZone("UTC")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val iso8601Format: DateFormat
|
||||
get() = requireNotNull(iso8601Holder.get()) { "Unable to find valid dateformatter" }
|
||||
|
||||
@JvmStatic
|
||||
fun toIso8601(date: Date): String {
|
||||
return iso8601Format.format(date)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun fromIso8601(date: String): Date {
|
||||
return try {
|
||||
iso8601Format.parse(date) ?: throw ParseException("DateFormat.parse returned null", 0)
|
||||
} catch (exc: ParseException) {
|
||||
throw IllegalArgumentException("Failed to parse timestamp", exc)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ import com.bugsnag.android.EndpointConfiguration
|
|||
import com.bugsnag.android.ErrorTypes
|
||||
import com.bugsnag.android.EventPayload
|
||||
import com.bugsnag.android.Logger
|
||||
import com.bugsnag.android.ManifestConfigLoader
|
||||
import com.bugsnag.android.ManifestConfigLoader.Companion.BUILD_UUID
|
||||
import com.bugsnag.android.NoopLogger
|
||||
import com.bugsnag.android.ThreadSendPolicy
|
||||
import com.bugsnag.android.errorApiHeaders
|
||||
|
@ -52,7 +52,8 @@ data class ImmutableConfig(
|
|||
|
||||
// results cached here to avoid unnecessary lookups in Client.
|
||||
val packageInfo: PackageInfo?,
|
||||
val appInfo: ApplicationInfo?
|
||||
val appInfo: ApplicationInfo?,
|
||||
val redactedKeys: Collection<String>
|
||||
) {
|
||||
|
||||
@JvmName("getErrorApiDeliveryParams")
|
||||
|
@ -162,7 +163,8 @@ internal fun convertToImmutableConfig(
|
|||
persistenceDirectory = persistenceDir,
|
||||
sendLaunchCrashesSynchronously = config.sendLaunchCrashesSynchronously,
|
||||
packageInfo = packageInfo,
|
||||
appInfo = appInfo
|
||||
appInfo = appInfo,
|
||||
redactedKeys = config.redactedKeys.toSet()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -209,7 +211,7 @@ internal fun sanitiseConfiguration(
|
|||
}
|
||||
|
||||
// populate buildUUID from manifest
|
||||
val buildUuid = appInfo?.metaData?.getString(ManifestConfigLoader.BUILD_UUID)
|
||||
val buildUuid = populateBuildUuid(appInfo)
|
||||
|
||||
@Suppress("SENSELESS_COMPARISON")
|
||||
if (configuration.delivery == null) {
|
||||
|
@ -224,5 +226,15 @@ internal fun sanitiseConfiguration(
|
|||
)
|
||||
}
|
||||
|
||||
private fun populateBuildUuid(appInfo: ApplicationInfo?): String? {
|
||||
val bundle = appInfo?.metaData
|
||||
return when {
|
||||
bundle?.containsKey(BUILD_UUID) == true -> {
|
||||
bundle.getString(BUILD_UUID) ?: bundle.getInt(BUILD_UUID).toString()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
internal const val RELEASE_STAGE_DEVELOPMENT = "development"
|
||||
internal const val RELEASE_STAGE_PRODUCTION = "production"
|
||||
|
|
Loading…
Reference in New Issue