Skip to content

Commit

Permalink
Merge pull request #2067 from bugsnag/release/v6.7.0
Browse files Browse the repository at this point in the history
Release v6.7.0
  • Loading branch information
lemnik committed Aug 8, 2024
2 parents d1d2eee + 2243aed commit 874d9e3
Show file tree
Hide file tree
Showing 52 changed files with 792 additions and 256 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/downstream_updates.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
RELEASE_VERSION: ${{ github.event_name == 'workflow_dispatch' && inputs.target_version || github.event.release.tag_name }}
strategy:
matrix:
downstream_repo: ['bugsnag/bugsnag-unity']
downstream_repo: ['bugsnag/bugsnag-unity', 'bugsnag/bugsnag-js', 'bugsnag/bugsnag-flutter', 'bugsnag/bugsnag-unreal']
steps:
- name: Install libcurl4-openssl-dev and net-tools
run: |
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Changelog

## 6.7.0 (2024-08-08)

### Enhancements

* Include additional Intent information for Activity.onCreate breadcrumbs (action, categories, type, flags, id, extra keys)
[#2057](https://github.com/bugsnag/bugsnag-android/pull/2057)
* New APIs allowing new `Error`s, `Thread`s, and `Stackframe`s to be added to `Event`s
[#2060](https://github.com/bugsnag/bugsnag-android/pull/2060)

### Bug fixes

* Handle rare cases where we need to deserialize threads that don't have a valid `state` property
[#2058](https://github.com/bugsnag/bugsnag-android/pull/2058)
* Avoid racing ourselves in the `bugsnag-plugin-android-ndk` during multi-threaded startups
[#2064](https://github.com/bugsnag/bugsnag-android/pull/2064)
* Fixed a timestamp formatting issue that caused NDK crash breadcrumbs to be dropped
[#2066](https://github.com/bugsnag/bugsnag-android/pull/2066)

## 6.6.1 (2024-07-03)

### Bug fixes
Expand Down
10 changes: 10 additions & 0 deletions bugsnag-android-core/api/bugsnag-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ public final class com/bugsnag/android/EndpointConfiguration {
}

public class com/bugsnag/android/Error : com/bugsnag/android/JsonStream$Streamable {
public fun addStackframe (Ljava/lang/String;Ljava/lang/String;J)Lcom/bugsnag/android/Stackframe;
public fun getErrorClass ()Ljava/lang/String;
public fun getErrorMessage ()Ljava/lang/String;
public fun getStacktrace ()Ljava/util/List;
Expand Down Expand Up @@ -350,11 +351,16 @@ public final class com/bugsnag/android/ErrorTypes {
}

public class com/bugsnag/android/Event : com/bugsnag/android/FeatureFlagAware, com/bugsnag/android/JsonStream$Streamable, com/bugsnag/android/MetadataAware, com/bugsnag/android/UserAware {
public fun addError (Ljava/lang/String;Ljava/lang/String;)Lcom/bugsnag/android/Error;
public fun addError (Ljava/lang/String;Ljava/lang/String;Lcom/bugsnag/android/ErrorType;)Lcom/bugsnag/android/Error;
public fun addError (Ljava/lang/Throwable;)Lcom/bugsnag/android/Error;
public fun addFeatureFlag (Ljava/lang/String;)V
public fun addFeatureFlag (Ljava/lang/String;Ljava/lang/String;)V
public fun addFeatureFlags (Ljava/lang/Iterable;)V
public fun addMetadata (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V
public fun addMetadata (Ljava/lang/String;Ljava/util/Map;)V
public fun addThread (JLjava/lang/String;)Lcom/bugsnag/android/Thread;
public fun addThread (Ljava/lang/String;Ljava/lang/String;)Lcom/bugsnag/android/Thread;
public fun clearFeatureFlag (Ljava/lang/String;)V
public fun clearFeatureFlags ()V
public fun clearMetadata (Ljava/lang/String;)V
Expand All @@ -374,6 +380,8 @@ public class com/bugsnag/android/Event : com/bugsnag/android/FeatureFlagAware, c
public fun getThreads ()Ljava/util/List;
public fun getUser ()Lcom/bugsnag/android/User;
public fun isUnhandled ()Z
public fun leaveBreadcrumb (Ljava/lang/String;)Lcom/bugsnag/android/Breadcrumb;
public fun leaveBreadcrumb (Ljava/lang/String;Lcom/bugsnag/android/BreadcrumbType;Ljava/util/Map;)Lcom/bugsnag/android/Breadcrumb;
public fun setApiKey (Ljava/lang/String;)V
public fun setContext (Ljava/lang/String;)V
public fun setGroupingHash (Ljava/lang/String;)V
Expand Down Expand Up @@ -762,6 +770,7 @@ public final class com/bugsnag/android/Telemetry : java/lang/Enum {
}

public class com/bugsnag/android/Thread : com/bugsnag/android/JsonStream$Streamable {
public fun addStackframe (Ljava/lang/String;Ljava/lang/String;J)Lcom/bugsnag/android/Stackframe;
public fun getErrorReportingThread ()Z
public fun getId ()Ljava/lang/String;
public fun getName ()Ljava/lang/String;
Expand Down Expand Up @@ -792,6 +801,7 @@ public final class com/bugsnag/android/Thread$State : java/lang/Enum {
}

public final class com/bugsnag/android/ThreadInternal : com/bugsnag/android/JsonStream$Streamable {
public final fun addStackframe (Ljava/lang/String;Ljava/lang/String;J)Lcom/bugsnag/android/Stackframe;
public final fun getId ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getStacktrace ()Ljava/util/List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.bugsnag.android.internal.DateUtils;
import com.bugsnag.android.internal.StateObserver;

import androidx.annotation.NonNull;
Expand Down Expand Up @@ -176,6 +177,8 @@ public void testLeaveStringBreadcrumbSendsMessage() {
assertEquals(BreadcrumbType.MANUAL, crumb.type);
assertEquals("Drift 4 units left", crumb.message);
assertTrue(crumb.metadata.isEmpty());
// DateUtils.fromIso8601 throws an exception on failure, but we also check for nulls
assertNotNull(DateUtils.fromIso8601(crumb.timestamp));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package com.bugsnag.android

import android.app.Activity
import android.app.Application
import android.content.Intent
import android.os.Build
import android.os.Bundle
import java.util.WeakHashMap

Expand All @@ -11,8 +13,16 @@ internal class ActivityBreadcrumbCollector(

private val prevState = WeakHashMap<Activity, String>()

override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) =
leaveBreadcrumb(activity, "onCreate()", savedInstanceState != null)
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
leaveBreadcrumb(
activity,
"onCreate()",
mutableMapOf<String, Any>().apply {
set("hasBundle", savedInstanceState != null)
setActivityIntentMetadata(activity.intent)
}
)
}

override fun onActivityStarted(activity: Activity) =
leaveBreadcrumb(activity, "onStart()")
Expand All @@ -39,12 +49,8 @@ internal class ActivityBreadcrumbCollector(
private fun leaveBreadcrumb(
activity: Activity,
lifecycleCallback: String,
hasBundle: Boolean? = null
metadata: MutableMap<String, Any> = mutableMapOf()
) {
val metadata = mutableMapOf<String, Any>()
if (hasBundle != null) {
metadata["hasBundle"] = hasBundle
}
val previousVal = prevState[activity]

if (previousVal != null) {
Expand All @@ -55,4 +61,24 @@ internal class ActivityBreadcrumbCollector(
cb("$activityName#$lifecycleCallback", metadata)
prevState[activity] = lifecycleCallback
}

private fun MutableMap<String, Any>.setActivityIntentMetadata(intent: Intent?) {
if (intent == null) return

intent.action?.let { set("action", it) }
intent.categories?.let { set("categories", it.joinToString(", ")) }
intent.type?.let { set("type", it) }

if (intent.flags != 0) {
@Suppress("MagicNumber") // hex radix
set("flags", "0x${intent.flags.toString(16)}")
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
intent.identifier?.let { set("id", it) }
}

set("hasData", intent.data != null)
set("hasExtras", intent.extras?.keySet()?.joinToString(", ") ?: false)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bugsnag.android

import com.bugsnag.android.internal.DateUtils
import java.io.IOException
import java.util.concurrent.atomic.AtomicInteger

Expand Down Expand Up @@ -42,8 +43,7 @@ internal class BreadcrumbState(
StateEvent.AddBreadcrumb(
breadcrumb.impl.message,
breadcrumb.impl.type,
// an encoding of milliseconds since the epoch
"t${breadcrumb.impl.timestamp.time}",
DateUtils.toIso8601(breadcrumb.impl.timestamp),
breadcrumb.impl.metadata ?: mutableMapOf()
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,14 +195,14 @@ internal class BugsnagEventMapper(
thread.readEntry("name"),
ErrorType.fromDescriptor(thread.readEntry("type")) ?: ErrorType.ANDROID,
thread["errorReportingThread"] == true,
thread.readEntry("state"),
thread["state"] as? String ?: "",
(thread["stacktrace"] as? List<Map<String, Any?>>)?.let { convertStacktrace(it) }
?: Stacktrace(emptyList())
?: Stacktrace(mutableListOf())
)
}

internal fun convertStacktrace(trace: List<Map<String, Any?>>): Stacktrace {
return Stacktrace(trace.map { Stackframe(it) })
return Stacktrace(trace.mapTo(ArrayList(trace.size)) { Stackframe(it) })
}

internal fun deserializeSeverityReason(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,16 @@ public List<Stackframe> getStacktrace() {
return impl.getStacktrace();
}

/**
* Add a new stackframe to the end of this Error returning the new Stackframe data object.
*/
@NonNull
public Stackframe addStackframe(@Nullable String method,
@Nullable String file,
long lineNumber) {
return impl.addStackframe(method, file, lineNumber);
}

@Override
public void toStream(@NonNull JsonStream stream) throws IOException {
impl.toStream(stream);
Expand All @@ -98,4 +108,4 @@ static List<Error> createError(@NonNull Throwable exc,
@NonNull Logger logger) {
return ErrorInternal.Companion.createError(exc, projectPackages, logger);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,30 @@ internal class ErrorInternal @JvmOverloads internal constructor(
var type: ErrorType = ErrorType.ANDROID
) : JsonStream.Streamable {

val stacktrace: List<Stackframe> = stacktrace.trace
val stacktrace: MutableList<Stackframe> = stacktrace.trace

fun addStackframe(method: String?, file: String?, lineNumber: Long): Stackframe {
val frame = Stackframe(method, file, lineNumber, null)
stacktrace.add(frame)
return frame
}

internal companion object {
fun createError(exc: Throwable, projectPackages: Collection<String>, logger: Logger): MutableList<Error> {
fun createError(
exc: Throwable,
projectPackages: Collection<String>,
logger: Logger
): MutableList<Error> {
return exc.safeUnrollCauses()
.mapTo(mutableListOf()) { currentEx ->
// Somehow it's possible for stackTrace to be null in rare cases
val stacktrace = currentEx.stackTrace ?: arrayOf<StackTraceElement>()
val trace = Stacktrace(stacktrace, projectPackages, logger)
val errorInternal =
ErrorInternal(currentEx.javaClass.name, currentEx.localizedMessage, trace)
val errorInternal = ErrorInternal(
currentEx.javaClass.name,
currentEx.localizedMessage,
trace
)

return@mapTo Error(errorInternal, logger)
}
Expand Down
Loading

0 comments on commit 874d9e3

Please sign in to comment.