Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v5.22.0 #1643

Merged
merged 32 commits into from
Mar 31, 2022
Merged

v5.22.0 #1643

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cac2d7f
Bump actions
Goooler Mar 17, 2022
ac7df49
Add actions for cache on CI
Goooler Mar 17, 2022
02512af
Add button to clear all persistent app data
twometresteve Mar 17, 2022
9f8fb3e
Clear persistent data before all scenarios
twometresteve Mar 17, 2022
93f59ec
Reposition Clear App Data button
twometresteve Mar 17, 2022
900455e
Merge branch 'next' into ci
twometresteve Mar 17, 2022
609d3ea
Merge pull request #1631 from bugsnag/goooler/ci
twometresteve Mar 17, 2022
5a8ab47
Fix lint error
twometresteve Mar 17, 2022
61f64e2
Merge branch 'next' into tms/clear-data
twometresteve Mar 17, 2022
58b077c
Merge pull request #1632 from bugsnag/tms/clear-data
twometresteve Mar 18, 2022
edcb70f
fix(ndk): bsg_strncpy correctly terminates `dst` even if `src` is `NULL`
lemnik Mar 23, 2022
82d3513
Merge pull request #1637 from bugsnag/PLAT-8208/strncpy-fix
lemnik Mar 23, 2022
22fc60e
fix: `Bugsnag.getClient` now synchronizes on `lock` so that `Bugsnag`…
lemnik Mar 23, 2022
d6d3b48
Merge pull request #1638 from bugsnag/PLAT-4420/bugsnag-start-thread-…
lemnik Mar 24, 2022
45e04f2
Ensure handling_crash flag is atomic
kstenerud Mar 28, 2022
3bb8d79
Merge pull request #1639 from bugsnag/PLAT-8095-atomic-handling-crash
kstenerud Mar 29, 2022
8e77540
Add Bugsnag.isStarted
slack-jallen Mar 11, 2022
f3e35c4
docs: update changelog with isStarted usage
tomlongridge Mar 29, 2022
a427799
test: fixed mirror test with new Bugsnag method name
tomlongridge Mar 29, 2022
8e327e0
[full ci] Limit retries of event payloads based on size and age, and …
kstenerud Mar 21, 2022
f74d35a
Merge pull request #1640 from bugsnag/jallen/bugsnag-isstarted
tomlongridge Mar 30, 2022
c0368d5
fix: `MultiThreadedStartupScenario` now includes a very small delay o…
lemnik Mar 30, 2022
39cd765
Merge branch 'next' into PLAT-8185-delete-old-big-events
kstenerud Mar 30, 2022
0dad273
Merge branch 'next' into PLAT_8247/fix-bugsnag-start-threading
lemnik Mar 30, 2022
51b7eae
Merge pull request #1633 from bugsnag/PLAT-8185-delete-old-big-events
kstenerud Mar 30, 2022
806afbe
Merge branch 'next' into PLAT_8247/fix-bugsnag-start-threading
lemnik Mar 30, 2022
450e931
Merge pull request #1641 from bugsnag/PLAT_8247/fix-bugsnag-start-thr…
lemnik Mar 30, 2022
7c0a730
Consistently format feature files
twometresteve Mar 30, 2022
b4c253d
Merge pull request #1635 from bugsnag/tms/reformat-features
twometresteve Mar 30, 2022
d2b4928
Rename Cucumber step for consistency with Cocoa
twometresteve Mar 30, 2022
c1bea01
Merge pull request #1642 from bugsnag/tms/rename-step
twometresteve Mar 31, 2022
eeb518a
v5.22.0
lemnik Mar 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: instrumentation_tests

on: [push, pull_request]
on: [ push, pull_request ]

env:
GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false -Dorg.gradle.parallel=true"
Expand All @@ -16,16 +16,29 @@ jobs:
- 29

steps:
- uses: actions/checkout@v2.3.5
- uses: actions/checkout@v3
with:
submodules: recursive
- uses: gradle/wrapper-validation-action@v1.0.4
- uses: gradle/wrapper-validation-action@v1

- uses: actions/setup-java@v2
- uses: actions/setup-java@v3
with:
distribution: 'zulu'
java-version: 11

- name: Gradle cache
uses: gradle/gradle-build-action@v2

- name: Create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
script: echo "Generated AVD snapshot for caching."

- name: Run Tests
uses: reactivecircus/android-emulator-runner@v2
with:
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

## 5.22.0 (2022-03-31)

### Enhancements

* Added `Bugsnag.isStarted()` to test whether the Bugsnag client is in the middle of initializing. This can be used to guard uses of the Bugsnag API that are either on separate threads early in the app's start-up and so not guaranteed to be executed after `Bugsnag.start` has completed, or where Bugsnag may not have been started at all due to some internal app logic.
[slack-jallen](https://github.com/slack-jallen):[#1621](https://github.com/bugsnag/bugsnag-android/pull/1621)
[#1640](https://github.com/bugsnag/bugsnag-android/pull/1640)

* Events and Sessions will be discarded if they cannot be uploaded and are older than 60 days or larger than 1MB
[#1633](https://github.com/bugsnag/bugsnag-android/pull/1633)

### Bug fixes

* Fixed potentially [thread-unsafe access](https://github.com/bugsnag/bugsnag-android/issues/883) when invoking `Bugsnag` static methods across different threads whilst `Bugsnag.start` is still in-flight. It is now safe to call any `Bugsnag` static method once `Bugsnag.start` has _begun_ executing, as access to the client singleton is controlled by a lock, so the new `isStarted` method (see above) should only be required where it cannot be determined whether the call to `Bugsnag.start` has begun or you do not want to wait. [#1638](https://github.com/bugsnag/bugsnag-android/pull/1638)
* Calling `bugsnag_event_set_context` with NULL `context` correctly clears the event context again
[#1637](https://github.com/bugsnag/bugsnag-android/pull/1637)

## 5.21.0 (2022-03-17)

### Enhancements
Expand Down
3 changes: 3 additions & 0 deletions bugsnag-android-core/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$429</ID>
<ID>MagicNumber:DefaultDelivery.kt$DefaultDelivery$499</ID>
<ID>MagicNumber:LastRunInfoStore.kt$LastRunInfoStore$3</ID>
<ID>MagicNumber:SessionFilenameInfo.kt$SessionFilenameInfo.Companion$35</ID>
<ID>MaxLineLength:LastRunInfo.kt$LastRunInfo$return "LastRunInfo(consecutiveLaunchCrashes=$consecutiveLaunchCrashes, crashed=$crashed, crashedDuringLaunch=$crashedDuringLaunch)"</ID>
<ID>MaxLineLength:ThreadState.kt$ThreadState$"[${allThreads.size - maxThreadCount} threads omitted as the maxReportedThreads limit ($maxThreadCount) was exceeded]"</ID>
<ID>ProtectedMemberInFinalClass:ConfigInternal.kt$ConfigInternal$protected val plugins = HashSet&lt;Plugin>()</ID>
Expand All @@ -38,7 +39,9 @@
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get battery status") }</ID>
<ID>SwallowedException:DeviceDataCollector.kt$DeviceDataCollector$catch (exception: Exception) { logger.w("Could not get locationStatus") }</ID>
<ID>SwallowedException:DeviceIdStore.kt$DeviceIdStore$catch (exc: OverlappingFileLockException) { Thread.sleep(FILE_LOCK_WAIT_MS) }</ID>
<ID>SwallowedException:EventFilenameInfo.kt$EventFilenameInfo.Companion$catch (e: Exception) { return -1 }</ID>
<ID>SwallowedException:PluginClient.kt$PluginClient$catch (exc: ClassNotFoundException) { logger.d("Plugin '$clz' is not on the classpath - functionality will not be enabled.") null }</ID>
<ID>SwallowedException:SessionFilenameInfo.kt$SessionFilenameInfo.Companion$catch (e: Exception) { return 0 }</ID>
<ID>TooManyFunctions:ConfigInternal.kt$ConfigInternal : CallbackAwareMetadataAwareUserAwareFeatureFlagAware</ID>
<ID>TooManyFunctions:EventInternal.kt$EventInternal : FeatureFlagAwareStreamableMetadataAwareUserAware</ID>
<ID>UnnecessaryAbstractClass:DependencyModule.kt$DependencyModule</ID>
Expand Down
59 changes: 38 additions & 21 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/Bugsnag.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,35 @@ public static Client start(@NonNull Context androidContext, @NonNull Configurati
return client;
}

/**
* Returns true if one of the <code>start</code> methods have been has been called and
* so Bugsnag is initialized; false if <code>start</code> has not been called and the
* other methods will throw IllegalStateException.
*/
public static boolean isStarted() {
return client != null;
}

private static void logClientInitWarning() {
getClient().logger.w("Multiple Bugsnag.start calls detected. Ignoring.");
}

/**
* Bugsnag uses the concept of "contexts" to help display and group your errors. Contexts
* represent what was happening in your application at the time an error occurs.
*
* <p>
* In an android app the "context" is automatically set as the foreground Activity.
* If you would like to set this value manually, you should alter this property.
*/
@Nullable public static String getContext() {
@Nullable
public static String getContext() {
return getClient().getContext();
}

/**
* Bugsnag uses the concept of "contexts" to help display and group your errors. Contexts
* represent what was happening in your application at the time an error occurs.
*
* <p>
* In an android app the "context" is automatically set as the foreground Activity.
* If you would like to set this value manually, you should alter this property.
*/
Expand Down Expand Up @@ -115,15 +125,15 @@ public static User getUser() {
/**
* Add a "on error" callback, to execute code at the point where an error report is
* captured in Bugsnag.
*
* <p>
* You can use this to add or modify information attached to an Event
* before it is sent to your dashboard. You can also return
* <code>false</code> from any callback to prevent delivery. "on error"
* callbacks do not run before reports generated in the event
* of immediate app termination from crashes in C/C++ code.
*
* <p>
* For example:
*
* <p>
* Bugsnag.addOnError(new OnErrorCallback() {
* public boolean run(Event event) {
* event.setSeverity(Severity.INFO);
Expand All @@ -140,6 +150,7 @@ public static void addOnError(@NonNull OnErrorCallback onError) {

/**
* Removes a previously added "on error" callback
*
* @param onError the callback to remove
*/
public static void removeOnError(@NonNull OnErrorCallback onError) {
Expand All @@ -149,12 +160,12 @@ public static void removeOnError(@NonNull OnErrorCallback onError) {
/**
* Add an "on breadcrumb" callback, to execute code before every
* breadcrumb captured by Bugsnag.
*
* <p>
* You can use this to modify breadcrumbs before they are stored by Bugsnag.
* You can also return <code>false</code> from any callback to ignore a breadcrumb.
*
* <p>
* For example:
*
* <p>
* Bugsnag.onBreadcrumb(new OnBreadcrumbCallback() {
* public boolean run(Breadcrumb breadcrumb) {
* return false; // ignore the breadcrumb
Expand All @@ -170,6 +181,7 @@ public static void addOnBreadcrumb(@NonNull final OnBreadcrumbCallback onBreadcr

/**
* Removes a previously added "on breadcrumb" callback
*
* @param onBreadcrumb the callback to remove
*/
public static void removeOnBreadcrumb(@NonNull OnBreadcrumbCallback onBreadcrumb) {
Expand All @@ -179,12 +191,12 @@ public static void removeOnBreadcrumb(@NonNull OnBreadcrumbCallback onBreadcrumb
/**
* Add an "on session" callback, to execute code before every
* session captured by Bugsnag.
*
* <p>
* You can use this to modify sessions before they are stored by Bugsnag.
* You can also return <code>false</code> from any callback to ignore a session.
*
* <p>
* For example:
*
* <p>
* Bugsnag.onSession(new OnSessionCallback() {
* public boolean run(Session session) {
* return false; // ignore the session
Expand All @@ -200,6 +212,7 @@ public static void addOnSession(@NonNull OnSessionCallback onSession) {

/**
* Removes a previously added "on session" callback
*
* @param onSession the callback to remove
*/
public static void removeOnSession(@NonNull OnSessionCallback onSession) {
Expand All @@ -219,7 +232,7 @@ public static void notify(@NonNull final Throwable exception) {
* Notify Bugsnag of a handled exception
*
* @param exception the exception to send to Bugsnag
* @param onError callback invoked on the generated error report for
* @param onError callback invoked on the generated error report for
* additional modification
*/
public static void notify(@NonNull final Throwable exception,
Expand Down Expand Up @@ -286,7 +299,8 @@ public static void leaveBreadcrumb(@NonNull String message) {
/**
* Leave a "breadcrumb" log message representing an action or event which
* occurred in your app, to aid with debugging
* @param message A short label
*
* @param message A short label
* @param metadata Additional diagnostic information about the app environment
* @param type A category for the breadcrumb
*/
Expand Down Expand Up @@ -332,11 +346,10 @@ public static void startSession() {
* <a href="https://docs.bugsnag.com/product/releases/releases-dashboard/#stability-score">
* stability score</a>.
*
* @return true if a previous session was resumed, false if a new session was started.
* @see #startSession()
* @see #pauseSession()
* @see Configuration#setAutoTrackSessions(boolean)
*
* @return true if a previous session was resumed, false if a new session was started.
*/
public static boolean resumeSession() {
return getClient().resumeSession();
Expand Down Expand Up @@ -365,7 +378,7 @@ public static void pauseSession() {
* Returns the current buffer of breadcrumbs that will be sent with captured events. This
* ordered list represents the most recent breadcrumbs to be captured up to the limit
* set in {@link Configuration#getMaxBreadcrumbs()}.
*
* <p>
* The returned collection is readonly and mutating the list will cause no effect on the
* Client's state. If you wish to alter the breadcrumbs collected by the Client then you should
* use {@link Configuration#setEnabledBreadcrumbTypes(Set)} and
Expand All @@ -380,7 +393,7 @@ public static List<Breadcrumb> getBreadcrumbs() {

/**
* Retrieves information about the last launch of the application, if it has been run before.
*
* <p>
* For example, this allows checking whether the app crashed on its last launch, which could
* be used to perform conditional behaviour to recover from crashes, such as clearing the
* app data cache.
Expand All @@ -394,7 +407,7 @@ public static LastRunInfo getLastRunInfo() {
* Informs Bugsnag that the application has finished launching. Once this has been called
* {@link AppWithState#isLaunching()} will always be false in any new error reports,
* and synchronous delivery will not be attempted on the next launch for any fatal crashes.
*
* <p>
* By default this method will be called after Bugsnag is initialized when
* {@link Configuration#getLaunchDurationMillis()} has elapsed. Invoking this method manually
* has precedence over the value supplied via the launchDurationMillis configuration option.
Expand Down Expand Up @@ -462,8 +475,12 @@ public static void clearFeatureFlags() {
@NonNull
public static Client getClient() {
if (client == null) {
throw new IllegalStateException("You must call Bugsnag.start before any"
+ " other Bugsnag methods");
synchronized (lock) {
if (client == null) {
throw new IllegalStateException("You must call Bugsnag.start before any"
+ " other Bugsnag methods");
}
}
}

return client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ internal data class EventFilenameInfo(
val errorTypes: Set<ErrorType>
) {

/**
* Generates a filename for the Event in the format
* "[timestamp]_[apiKey]_[errorTypes]_[UUID]_[startupcrash|not-jvm].json"
*/
fun encode(): String {
return "${timestamp}_${apiKey}_${serializeErrorTypeHeader(errorTypes)}_${uuid}_$suffix.json"
return toFilename(apiKey, uuid, timestamp, suffix, errorTypes)
}

fun isLaunchCrashReport(): Boolean = suffix == STARTUP_CRASH
Expand All @@ -36,7 +32,21 @@ internal data class EventFilenameInfo(
private const val STARTUP_CRASH = "startupcrash"
private const val NON_JVM_CRASH = "not-jvm"

@JvmOverloads
/**
* Generates a filename for the Event in the format
* "[timestamp]_[apiKey]_[errorTypes]_[UUID]_[startupcrash|not-jvm].json"
*/
fun toFilename(
apiKey: String,
uuid: String,
timestamp: Long,
suffix: String,
errorTypes: Set<ErrorType>
): String {
return "${timestamp}_${apiKey}_${serializeErrorTypeHeader(errorTypes)}_${uuid}_$suffix.json"
}

@JvmOverloads @JvmStatic
fun fromEvent(
obj: Any,
uuid: String = UUID.randomUUID().toString(),
Expand All @@ -63,11 +73,12 @@ internal data class EventFilenameInfo(
/**
* Reads event information from a filename.
*/
@JvmStatic
fun fromFile(file: File, config: ImmutableConfig): EventFilenameInfo {
return EventFilenameInfo(
findApiKeyInFilename(file, config),
"", // ignore UUID field when reading from file as unused
-1, // ignore timestamp when reading from file as unused
findTimestampInFilename(file),
findSuffixInFilename(file),
findErrorTypesInFilename(file)
)
Expand All @@ -77,7 +88,7 @@ internal data class EventFilenameInfo(
* Retrieves the api key encoded in the filename, or an empty string if this information
* is not encoded for the given event
*/
private fun findApiKeyInFilename(file: File, config: ImmutableConfig): String {
internal fun findApiKeyInFilename(file: File, config: ImmutableConfig): String {
val name = file.name.removeSuffix("_$STARTUP_CRASH.json")
val start = name.indexOf("_") + 1
val end = name.indexOf("_", start)
Expand All @@ -93,7 +104,7 @@ internal data class EventFilenameInfo(
* Retrieves the error types encoded in the filename, or an empty string if this
* information is not encoded for the given event
*/
private fun findErrorTypesInFilename(eventFile: File): Set<ErrorType> {
internal fun findErrorTypesInFilename(eventFile: File): Set<ErrorType> {
val name = eventFile.name
val end = name.lastIndexOf("_", name.lastIndexOf("_") - 1)
val start = name.lastIndexOf("_", end - 1) + 1
Expand All @@ -111,7 +122,7 @@ internal data class EventFilenameInfo(
* Retrieves the error types encoded in the filename, or an empty string if this
* information is not encoded for the given event
*/
private fun findSuffixInFilename(eventFile: File): String {
internal fun findSuffixInFilename(eventFile: File): String {
val name = eventFile.nameWithoutExtension
val suffix = name.substring(name.lastIndexOf("_") + 1)
return when (suffix) {
Expand All @@ -120,10 +131,20 @@ internal data class EventFilenameInfo(
}
}

/**
* Retrieves the error types encoded in the filename, or an empty string if this
* information is not encoded for the given event
*/
@JvmStatic
fun findTimestampInFilename(eventFile: File): Long {
val name = eventFile.nameWithoutExtension
return name.substringBefore("_", missingDelimiterValue = "-1").toLongOrNull() ?: -1
}

/**
* Retrieves the error types for the given event
*/
private fun findErrorTypesForEvent(obj: Any): Set<ErrorType> {
internal fun findErrorTypesForEvent(obj: Any): Set<ErrorType> {
return when (obj) {
is Event -> obj.impl.getErrorTypesFromStackframes()
else -> setOf(ErrorType.C)
Expand All @@ -133,7 +154,7 @@ internal data class EventFilenameInfo(
/**
* Calculates the suffix for the given event
*/
private fun findSuffixForEvent(obj: Any, launching: Boolean?): String {
internal fun findSuffixForEvent(obj: Any, launching: Boolean?): String {
return when {
obj is Event && obj.app.isLaunching == true -> STARTUP_CRASH
launching == true -> STARTUP_CRASH
Expand Down
Loading