Skip to content

Commit 299ed13

Browse files
committed
POC for workflow abstraction
1 parent 17d8408 commit 299ed13

File tree

11 files changed

+113
-5
lines changed

11 files changed

+113
-5
lines changed

embrace-android-api/src/main/kotlin/io/embrace/android/embracesdk/spans/TracingApi.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,6 @@ public interface TracingApi {
264264
* if was completed in a prior session.
265265
*/
266266
public fun getSpan(spanId: String): EmbraceSpan?
267+
268+
public fun createOperation(name: String): Workflow
267269
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.embrace.android.embracesdk.spans
2+
3+
public interface Workflow {
4+
public fun start(segmentName: String? = null): Boolean
5+
public fun startSegment(segmentName: String): Boolean
6+
public fun end(errorCode: ErrorCode? = null): Boolean
7+
}

embrace-android-core/src/main/kotlin/io/embrace/android/embracesdk/internal/spans/EmbraceTracer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.embrace.android.embracesdk.spans.EmbraceSpan
66
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
77
import io.embrace.android.embracesdk.spans.ErrorCode
88
import io.embrace.android.embracesdk.spans.TracingApi
9+
import io.embrace.android.embracesdk.spans.Workflow
910

1011
class EmbraceTracer(
1112
private val spanService: SpanService,
@@ -78,4 +79,6 @@ class EmbraceTracer(
7879
)
7980

8081
override fun getSpan(spanId: String): EmbraceSpan? = spanService.getSpan(spanId = spanId)
82+
83+
override fun createOperation(name: String): Workflow = Operation(name = name, spanService = spanService)
8184
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.embrace.android.embracesdk.internal.spans
2+
3+
import io.embrace.android.embracesdk.spans.EmbraceSpan
4+
import io.embrace.android.embracesdk.spans.ErrorCode
5+
import io.embrace.android.embracesdk.spans.Workflow
6+
7+
class Operation(
8+
private val name: String,
9+
private val spanService: SpanService,
10+
) : Workflow {
11+
@Volatile
12+
private var root: EmbraceSpan? = null
13+
14+
@Volatile
15+
private var currentSegment: EmbraceSpan? = null
16+
17+
override fun start(segmentName: String?): Boolean {
18+
spanService.startSpan(name)?.apply {
19+
root = this
20+
segmentName?.let {
21+
startSegment(it)
22+
}
23+
}
24+
25+
return root != null && (segmentName == null || currentSegment != null)
26+
}
27+
28+
override fun startSegment(segmentName: String): Boolean {
29+
root?.run {
30+
currentSegment?.stop()
31+
currentSegment = spanService.startSpan(parent = root, name = "$name-$segmentName")
32+
}
33+
34+
return currentSegment != null
35+
}
36+
37+
override fun end(errorCode: ErrorCode?): Boolean {
38+
currentSegment?.stop(errorCode)
39+
return root?.stop(errorCode) != null
40+
}
41+
}

embrace-android-sdk/src/integrationTest/kotlin/io/embrace/android/embracesdk/testcases/TracingApiTest.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package io.embrace.android.embracesdk.testcases
33
import androidx.test.ext.junit.runners.AndroidJUnit4
44
import io.embrace.android.embracesdk.arch.assertIsTypePerformance
55
import io.embrace.android.embracesdk.assertions.assertEmbraceSpanData
6+
import io.embrace.android.embracesdk.assertions.findSpansByName
67
import io.embrace.android.embracesdk.concurrency.SingleThreadTestScheduledExecutor
78
import io.embrace.android.embracesdk.fakes.FakeSpanExporter
89
import io.embrace.android.embracesdk.fakes.config.FakeEnabledFeatureConfig
@@ -329,6 +330,35 @@ internal class TracingApiTest {
329330
)
330331
}
331332

333+
@Test
334+
fun `record operation`() {
335+
testRule.runTest(
336+
testCaseAction = {
337+
recordSession {
338+
clock.tick(100L)
339+
val op = embrace.createOperation("my-op")
340+
clock.tick(100L)
341+
op.start("first")
342+
clock.tick(100L)
343+
op.start("second")
344+
clock.tick(100L)
345+
op.start("third")
346+
clock.tick(100L)
347+
op.end(ErrorCode.FAILURE)
348+
}
349+
},
350+
assertAction = {
351+
with(getSingleSessionEnvelope()) {
352+
assertNotNull(findSpansByName(name = "my-op"))
353+
assertNotNull(findSpansByName(name = "my-op-first"))
354+
assertNotNull(findSpansByName(name = "my-op-second"))
355+
assertNotNull(findSpansByName(name = "my-op-third"))
356+
}
357+
}
358+
)
359+
}
360+
361+
332362
private fun EmbracePayloadAssertionInterface.getSdkInitSpanFromBackgroundActivity(): List<Span> {
333363
val lastSentBackgroundActivity = getSingleSessionEnvelope(ApplicationState.BACKGROUND)
334364
val spans = checkNotNull(lastSentBackgroundActivity.data.spans)

embrace-android-sdk/src/main/java/io/embrace/android/embracesdk/Embrace.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import io.embrace.android.embracesdk.spans.AutoTerminationMode
1313
import io.embrace.android.embracesdk.spans.EmbraceSpan
1414
import io.embrace.android.embracesdk.spans.EmbraceSpanEvent
1515
import io.embrace.android.embracesdk.spans.ErrorCode
16+
import io.embrace.android.embracesdk.spans.Workflow
1617
import io.opentelemetry.api.OpenTelemetry
1718
import io.opentelemetry.sdk.logs.export.LogRecordExporter
1819
import io.opentelemetry.sdk.trace.export.SpanExporter
@@ -377,6 +378,10 @@ public class Embrace private constructor(
377378
return impl.getSpan(spanId)
378379
}
379380

381+
override fun createOperation(name: String): Workflow {
382+
return impl.createOperation(name)
383+
}
384+
380385
/**
381386
* Adds a [SpanExporter] to the tracer.
382387
*

examples/ExampleApp/app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ android {
2727
getDefaultProguardFile("proguard-android-optimize.txt"),
2828
"proguard-rules.pro"
2929
)
30+
signingConfig = signingConfigs.getByName("debug")
3031
}
3132
}
3233
compileOptions {

examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/MainActivity.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,41 @@ import androidx.activity.enableEdgeToEdge
77
import androidx.navigation.compose.NavHost
88
import androidx.navigation.compose.composable
99
import androidx.navigation.compose.rememberNavController
10+
import io.embrace.android.embracesdk.Embrace
11+
import io.embrace.android.embracesdk.spans.Workflow
1012
import io.embrace.android.exampleapp.ui.CodeExample
1113
import io.embrace.android.exampleapp.ui.CodeExampleDetailScreen
1214
import io.embrace.android.exampleapp.ui.CodeExampleListScreen
1315
import io.embrace.android.exampleapp.ui.theme.ExampleAppTheme
1416

1517
class MainActivity : ComponentActivity() {
1618

19+
private val operation: Workflow = Embrace.getInstance().createOperation("app-screen")
20+
1721
override fun onCreate(savedInstanceState: Bundle?) {
1822
super.onCreate(savedInstanceState)
23+
operation.start("load")
1924
val examples = CodeExample.entries
2025
enableEdgeToEdge()
2126
setContent {
2227
ExampleAppTheme {
2328
val navController = rememberNavController()
2429
NavHost(navController = navController, startDestination = "main") {
2530
composable("main") {
26-
CodeExampleListScreen(navController, examples)
31+
CodeExampleListScreen(navController, examples, operation)
2732
}
2833
examples.forEach { example ->
2934
composable(example.route) {
30-
CodeExampleDetailScreen(navController, example)
35+
CodeExampleDetailScreen(navController, example, operation)
3136
}
3237
}
3338
}
3439
}
3540
}
3641
}
42+
43+
override fun onPause() {
44+
super.onPause()
45+
operation.end()
46+
}
3747
}

examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExampleDetailScreen.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ import androidx.compose.ui.Modifier
1717
import androidx.compose.ui.graphics.Color
1818
import androidx.compose.ui.unit.dp
1919
import androidx.navigation.NavController
20+
import io.embrace.android.embracesdk.spans.Workflow
2021

2122
@OptIn(ExperimentalMaterial3Api::class)
2223
@Composable
23-
fun CodeExampleDetailScreen(navController: NavController, example: CodeExample) {
24+
fun CodeExampleDetailScreen(navController: NavController, example: CodeExample, operation: Workflow? = null) {
2425
Scaffold(
2526
topBar = {
2627
TopAppBar(
@@ -29,7 +30,12 @@ fun CodeExampleDetailScreen(navController: NavController, example: CodeExample)
2930
},
3031
colors = appBarColors(),
3132
navigationIcon = {
32-
IconButton(onClick = navController::popBackStack) {
33+
IconButton(
34+
onClick = {
35+
operation?.startSegment("main")
36+
navController.popBackStack()
37+
}
38+
) {
3339
Icon(
3440
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
3541
contentDescription = "Back",

examples/ExampleApp/app/src/main/kotlin/io/embrace/android/exampleapp/ui/CodeExampleListScreen.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ import androidx.compose.ui.Modifier
1515
import androidx.compose.ui.res.stringResource
1616
import androidx.compose.ui.unit.dp
1717
import androidx.navigation.NavController
18+
import io.embrace.android.embracesdk.spans.Workflow
1819
import io.embrace.android.exampleapp.R
1920

2021
@OptIn(ExperimentalMaterial3Api::class)
2122
@Composable
2223
fun CodeExampleListScreen(
2324
navController: NavController,
2425
examples: List<CodeExample>,
26+
operation: Workflow?,
2527
) {
2628
Scaffold(modifier = Modifier.fillMaxSize(),
2729
topBar = {
@@ -37,6 +39,7 @@ fun CodeExampleListScreen(
3739
val item = examples[index]
3840
Button(
3941
onClick = {
42+
operation?.startSegment(item.route)
4043
navController.navigate(item.route)
4144
},
4245
modifier = Modifier

0 commit comments

Comments
 (0)