Skip to content

Commit

Permalink
Activity Open trace logging and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bidetofevil committed Aug 28, 2024
1 parent c14681e commit c336053
Show file tree
Hide file tree
Showing 20 changed files with 1,107 additions and 38 deletions.
21 changes: 21 additions & 0 deletions embrace-android-api/api/embrace-android-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,27 @@ public abstract interface class io/embrace/android/embracesdk/internal/api/UserA
public abstract fun setUsername (Ljava/lang/String;)V
}

public abstract interface class io/embrace/android/embracesdk/internal/capture/activity/OpenEvents {
public abstract fun create (ILjava/lang/String;J)V
public abstract fun createEnd (IJ)V
public abstract fun hibernate (ILjava/lang/String;J)V
public abstract fun render (ILjava/lang/String;J)V
public abstract fun renderEnd (IJ)V
public abstract fun resetTrace (ILjava/lang/String;J)V
public abstract fun resume (ILjava/lang/String;J)V
public abstract fun resumeEnd (IJ)V
public abstract fun start (ILjava/lang/String;J)V
public abstract fun startEnd (IJ)V
}

public final class io/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType : java/lang/Enum {
public static final field COLD Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public static final field HOT Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public final fun getTypeName ()Ljava/lang/String;
public static fun valueOf (Ljava/lang/String;)Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
public static fun values ()[Lio/embrace/android/embracesdk/internal/capture/activity/OpenEvents$OpenType;
}

public final class io/embrace/android/embracesdk/internal/network/http/NetworkCaptureData {
public fun <init> (Ljava/util/Map;Ljava/lang/String;[BLjava/util/Map;[BLjava/lang/String;)V
public synthetic fun <init> (Ljava/util/Map;Ljava/lang/String;[BLjava/util/Map;[BLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.embrace.android.embracesdk.internal.capture.activity

/**
* The relevant stages in the lifecycle of Activities pertaining to observing the performance of their loading
*/
public interface OpenEvents {

/**
* When a previously in-progress Activity Open trace should be abandoned, and that the component managing
* the trace recording should prepare itself to start tracing the opening of a new Activity instance.
*/
public fun resetTrace(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the app is no longer in a state where it is trying to open up a new Activity
*/
public fun hibernate(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity is entering the CREATE stage of its lifecycle.
*/
public fun create(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the CREATE stage of its lifecycle.
*/
public fun createEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity is entering the START stage of its lifecycle.
*/
public fun start(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the START stage of its lifecycle.
*/
public fun startEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity is entering the RESUME stage of its lifecycle.
*/
public fun resume(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity has exited the RESUME stage of its lifecycle.
*/
public fun resumeEnd(instanceId: Int, timestampMs: Long)

/**
* When the given Activity's first UI frame starts to be rendered.
*/
public fun render(instanceId: Int, activityName: String, timestampMs: Long)

/**
* When the given Activity's first UI frame has been displayed.
*/
public fun renderEnd(instanceId: Int, timestampMs: Long)

public enum class OpenType(public val typeName: String) {
COLD("cold"), HOT("hot")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public sealed class EmbType(type: String, subtype: String?) : TelemetryType {
public object NativeThreadBlockageSample : Performance("native_thread_blockage_sample")

public object ThermalState : Performance("thermal_state")

public object ActivityOpen : Performance("activity_open")
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ActivityLifecycleTracker(
/**
* List of listeners that subscribe to activity events.
*/
public val listeners: CopyOnWriteArrayList<ActivityLifecycleListener> =
public val activityListeners: CopyOnWriteArrayList<ActivityLifecycleListener> =
CopyOnWriteArrayList<ActivityLifecycleListener>()

/**
Expand Down Expand Up @@ -64,7 +64,7 @@ public class ActivityLifecycleTracker(

override fun onActivityCreated(activity: Activity, bundle: Bundle?) {
updateStateWithActivity(activity)
stream(listeners) { listener: ActivityLifecycleListener ->
stream(activityListeners) { listener: ActivityLifecycleListener ->
try {
listener.onActivityCreated(activity, bundle)
} catch (ex: Exception) {
Expand All @@ -76,7 +76,7 @@ public class ActivityLifecycleTracker(

override fun onActivityStarted(activity: Activity) {
updateStateWithActivity(activity)
stream(listeners) { listener: ActivityLifecycleListener ->
stream(activityListeners) { listener: ActivityLifecycleListener ->
try {
listener.onActivityStarted(activity)
} catch (ex: Exception) {
Expand All @@ -103,7 +103,7 @@ public class ActivityLifecycleTracker(

override fun onActivityPaused(activity: Activity) {}
override fun onActivityStopped(activity: Activity) {
stream(listeners) { listener: ActivityLifecycleListener ->
stream(activityListeners) { listener: ActivityLifecycleListener ->
try {
listener.onActivityStopped(activity)
} catch (ex: Exception) {
Expand All @@ -118,8 +118,8 @@ public class ActivityLifecycleTracker(
override fun onActivityDestroyed(activity: Activity) {}

override fun addListener(listener: ActivityLifecycleListener) {
if (!listeners.contains(listener)) {
listeners.addIfAbsent(listener)
if (!activityListeners.contains(listener)) {
activityListeners.addIfAbsent(listener)
}
}

Expand All @@ -133,7 +133,7 @@ public class ActivityLifecycleTracker(
try {
logger.logDebug("Shutting down ActivityLifecycleTracker")
application.unregisterActivityLifecycleCallbacks(this)
listeners.clear()
activityListeners.clear()
startupListeners.clear()
} catch (ex: Exception) {
logger.logWarning("Error when closing ActivityLifecycleTracker", ex)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public class EmbraceSpanBuilder(
}

private fun updateKeySpan() {
if (fixedAttributes.contains(EmbType.Performance.Default)) {
if (fixedAttributes.contains(EmbType.Performance.Default) || fixedAttributes.contains(EmbType.Performance.ActivityOpen)) {
if (getParentSpan() == null) {
fixedAttributes.add(KeySpan)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ internal class EmbraceSpanImpl(
}

override fun updateName(newName: String): Boolean {
if (newName.isValidName()) {
if (newName.isValidName(spanBuilder.internal)) {
synchronized(startedSpan) {
if (!spanStarted() || isRecording) {
updatedName = newName
Expand Down Expand Up @@ -313,10 +313,10 @@ internal class EmbraceSpanImpl(

private fun getSpanName() = synchronized(startedSpan) { updatedName ?: spanBuilder.spanName }

public companion object {
companion object {
internal fun attributeValid(key: String, value: String) =
key.length <= MAX_CUSTOM_ATTRIBUTE_KEY_LENGTH && value.length <= MAX_CUSTOM_ATTRIBUTE_VALUE_LENGTH

internal fun String.isValidName(): Boolean = isNotBlank() && (length <= MAX_NAME_LENGTH)
internal fun String.isValidName(internal: Boolean): Boolean = isNotBlank() && (internal || length <= MAX_NAME_LENGTH)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ internal class SpanServiceImpl(
internal: Boolean,
private: Boolean
): PersistableEmbraceSpan? {
return if (inputsValid(name) && currentSessionSpan.canStartNewSpan(parent, internal)) {
return if (inputsValid(name, internal) && currentSessionSpan.canStartNewSpan(parent, internal)) {
embraceSpanFactory.create(
name = name,
type = type,
Expand All @@ -49,7 +49,7 @@ internal class SpanServiceImpl(

override fun createSpan(embraceSpanBuilder: EmbraceSpanBuilder): PersistableEmbraceSpan? {
return if (
inputsValid(embraceSpanBuilder.spanName) &&
inputsValid(embraceSpanBuilder.spanName, embraceSpanBuilder.internal) &&
currentSessionSpan.canStartNewSpan(embraceSpanBuilder.getParentSpan(), embraceSpanBuilder.internal)
) {
embraceSpanFactory.create(embraceSpanBuilder)
Expand Down Expand Up @@ -110,7 +110,7 @@ internal class SpanServiceImpl(
return false
}

if (inputsValid(name, events, attributes) && currentSessionSpan.canStartNewSpan(parent, internal)) {
if (inputsValid(name, internal, events, attributes) && currentSessionSpan.canStartNewSpan(parent, internal)) {
val newSpan = embraceSpanFactory.create(name = name, type = type, internal = internal, private = private, parent = parent)
if (newSpan.start(startTimeMs)) {
attributes.forEach {
Expand All @@ -130,16 +130,17 @@ internal class SpanServiceImpl(

private fun inputsValid(
name: String,
internal: Boolean,
events: List<EmbraceSpanEvent>? = null,
attributes: Map<String, String>? = null
): Boolean {
return name.isValidName() &&
return (name.isValidName(internal)) &&
((events == null) || (events.size <= EmbraceSpanLimits.MAX_CUSTOM_EVENT_COUNT)) &&
((attributes == null) || (attributes.size <= EmbraceSpanLimits.MAX_CUSTOM_ATTRIBUTE_COUNT))
}

public companion object {
public const val MAX_INTERNAL_SPANS_PER_SESSION: Int = 5000
public const val MAX_NON_INTERNAL_SPANS_PER_SESSION: Int = 500
companion object {
const val MAX_INTERNAL_SPANS_PER_SESSION: Int = 5000
const val MAX_NON_INTERNAL_SPANS_PER_SESSION: Int = 500
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,12 +183,12 @@ internal class ActivityLifecycleTrackerTest {
@Test
fun `verify a listener is added`() {
// assert empty list first
assertEquals(0, activityLifecycleTracker.listeners.size)
assertEquals(0, activityLifecycleTracker.activityListeners.size)

val mockActivityLifecycleListener = mockk<ActivityLifecycleListener>()
activityLifecycleTracker.addListener(mockActivityLifecycleListener)

assertEquals(1, activityLifecycleTracker.listeners.size)
assertEquals(1, activityLifecycleTracker.activityListeners.size)
}

@Test
Expand All @@ -209,7 +209,7 @@ internal class ActivityLifecycleTrackerTest {
// add it for a 2nd time
activityLifecycleTracker.addListener(mockActivityLifecycleListener)

assertEquals(1, activityLifecycleTracker.listeners.size)
assertEquals(1, activityLifecycleTracker.activityListeners.size)
}

@Test
Expand All @@ -220,8 +220,8 @@ internal class ActivityLifecycleTrackerTest {

activityLifecycleTracker.addListener(mockActivityLifecycleListener2)

assertEquals(2, activityLifecycleTracker.listeners.size)
assertEquals(mockActivityLifecycleListener2, activityLifecycleTracker.listeners[1])
assertEquals(2, activityLifecycleTracker.activityListeners.size)
assertEquals(mockActivityLifecycleListener2, activityLifecycleTracker.activityListeners[1])
}

@Test
Expand All @@ -237,7 +237,7 @@ internal class ActivityLifecycleTrackerTest {
activityLifecycleTracker.close()

verify { application.unregisterActivityLifecycleCallbacks(activityLifecycleTracker) }
assertTrue(activityLifecycleTracker.listeners.isEmpty())
assertTrue(activityLifecycleTracker.activityListeners.isEmpty())
assertTrue(activityLifecycleTracker.startupListeners.isEmpty())
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,14 +473,14 @@ internal class SpanServiceImplTest {
}

@Test
fun `check name length limit`() {
assertNull(spansService.createSpan(name = TOO_LONG_SPAN_NAME))
assertFalse(spansService.recordCompletedSpan(name = TOO_LONG_SPAN_NAME, startTimeMs = 100L, endTimeMs = 200L))
assertNotNull(spansService.recordSpan(name = TOO_LONG_SPAN_NAME) { 1 })
fun `check name length limit for non-internal spans`() {
assertNull(spansService.createSpan(name = TOO_LONG_SPAN_NAME, internal = false))
assertFalse(spansService.recordCompletedSpan(name = TOO_LONG_SPAN_NAME, startTimeMs = 100L, endTimeMs = 200L, internal = false))
assertNotNull(spansService.recordSpan(name = TOO_LONG_SPAN_NAME, internal = false) { 1 })
assertEquals(0, spanSink.completedSpans().size)
assertNotNull(spansService.createSpan(name = MAX_LENGTH_SPAN_NAME))
assertNotNull(spansService.recordSpan(name = MAX_LENGTH_SPAN_NAME) { 2 })
assertTrue(spansService.recordCompletedSpan(name = MAX_LENGTH_SPAN_NAME, startTimeMs = 100L, endTimeMs = 200L))
assertNotNull(spansService.createSpan(name = MAX_LENGTH_SPAN_NAME, internal = false))
assertNotNull(spansService.recordSpan(name = MAX_LENGTH_SPAN_NAME, internal = false) { 2 })
assertTrue(spansService.recordCompletedSpan(name = MAX_LENGTH_SPAN_NAME, startTimeMs = 100L, endTimeMs = 200L, internal = false))
assertEquals(2, spanSink.completedSpans().size)
}

Expand Down
Loading

0 comments on commit c336053

Please sign in to comment.