Updated Bugsnag

This commit is contained in:
M66B 2021-10-12 08:52:09 +02:00
parent b5a61240b0
commit 90bcc0a965
19 changed files with 210 additions and 60 deletions

View File

@ -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

View File

@ -1,5 +1,7 @@
package com.bugsnag.android;
import com.bugsnag.android.internal.DateUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

View File

@ -1,5 +1,6 @@
package com.bugsnag.android
import com.bugsnag.android.internal.DateUtils
import java.io.IOException
import java.util.concurrent.atomic.AtomicInteger

View File

@ -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() {

View File

@ -21,7 +21,8 @@ internal class ClientObservable : BaseObservable() {
conf.buildUuid,
conf.releaseStage,
lastRunInfoPath,
consecutiveLaunchCrashes
consecutiveLaunchCrashes,
conf.sendThreads
)
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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();
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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);

View File

@ -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()

View File

@ -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;
}
}
}
}

View File

@ -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()

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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"