Skip to content

Commit

Permalink
extract HoneycombInstrumentedComposable to our distro
Browse files Browse the repository at this point in the history
include compose compile options
  • Loading branch information
MustafaHaddara committed Jan 9, 2025
1 parent 295d9cb commit 7c45b19
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 54 deletions.
8 changes: 8 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ android {
}
buildFeatures {
buildConfig = true
compose = true
}
buildTypes {
release {
Expand All @@ -40,6 +41,9 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
kotlinOptions {
jvmTarget = "1.8"
}
Expand All @@ -57,6 +61,10 @@ android {
}

dependencies {
implementation(libs.androidx.runtime.android)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))

// This is required by opentelemetry-android.
coreLibraryDesugaring(libs.desugar.jdk.libs)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.honeycomb.opentelemetry.android

import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import io.opentelemetry.api.OpenTelemetry
import java.time.Instant
import kotlin.time.TimeSource.Monotonic.markNow

/**
* Heavily inspired by https://github.com/theapache64/boil/blob/master/files/LogComposition.kt
*/
@Composable
fun HoneycombInstrumentedComposable(
name: String,
otelRum: OpenTelemetry,
composable: @Composable (() -> Unit),
) {
val tracer = otelRum.tracerProvider.tracerBuilder("io.honeycomb.render-instrumentation").build()
val span =
tracer
.spanBuilder("View Render")
.setAttribute("view.name", name)
.startSpan()

span.makeCurrent().use {
val bodySpan =
tracer
.spanBuilder("View Body")
.setAttribute("view.name", name)
.startSpan()

bodySpan.makeCurrent().use {
val start = markNow()
composable()
val endTime = Instant.now()

val bodyDuration = start.elapsedNow()
// bodyDuration is in seconds
// calling duration.inWholeSeconds would lose precision
span.setAttribute("view.renderDuration", bodyDuration.inWholeMicroseconds / 1_000_000.toDouble())

SideEffect {
bodySpan.end(endTime)
val renderDuration = start.elapsedNow()
span.setAttribute("view.totalDuration", renderDuration.inWholeMicroseconds / 1_000_000.toDouble())
span.end()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
Expand All @@ -18,61 +17,17 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.tooling.preview.Preview
import io.honeycomb.opentelemetry.android.HoneycombInstrumentedComposable
import io.honeycomb.opentelemetry.android.example.ui.theme.HoneycombOpenTelemetryAndroidTheme
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.Instant
import kotlin.time.TimeSource.Monotonic.markNow

private const val TAG = "ViewInstrumentation"

/**
* Heavily inspired by https://github.com/theapache64/boil/blob/master/files/LogComposition.kt
*/
@Composable
@Suppress("NOTHING_TO_INLINE")
private inline fun HoneycombInstrumentedComposable(
name: String,
composable: @Composable (() -> Unit),
) {
val tracer = LocalOtelComposition.current!!.openTelemetry.tracerProvider.tracerBuilder("io.honeycomb.render-instrumentation").build()
val span =
tracer
.spanBuilder("View Render")
.setAttribute("view.name", name)
.startSpan()

span.makeCurrent().use {
val bodySpan =
tracer
.spanBuilder("View Body")
.setAttribute("view.name", name)
.startSpan()

bodySpan.makeCurrent().use {
val start = markNow()
composable()
val endTime = Instant.now()

val bodyDuration = start.elapsedNow()
// bodyDuration is in seconds
// calling duration.inWholeSeconds would lose precision
span.setAttribute("view.renderDuration", bodyDuration.inWholeMicroseconds / 1_000_000.toDouble())

SideEffect {
bodySpan.end(endTime)
val renderDuration = start.elapsedNow()
span.setAttribute("view.totalDuration", renderDuration.inWholeMicroseconds / 1_000_000.toDouble())
span.end()
}
}
}
}

@Composable
private fun NestedExpensiveView(delayMs: Long) {
Row {
HoneycombInstrumentedComposable("nested expensive text") {
HoneycombInstrumentedComposable("nested expensive text", LocalOtelComposition.current!!.openTelemetry) {
Text(text = timeConsumingCalculation(delayMs))
}
}
Expand All @@ -97,31 +52,31 @@ private fun DelayedSlider(
private fun ExpensiveView() {
val (delay, setDelay) = remember { mutableLongStateOf(1000L) }

HoneycombInstrumentedComposable("main view") {
HoneycombInstrumentedComposable("main view", LocalOtelComposition.current!!.openTelemetry) {
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth(),
) {
DelayedSlider(delay = delay, onValueChange = setDelay)

HoneycombInstrumentedComposable("expensive text 1") {
HoneycombInstrumentedComposable("expensive text 1", LocalOtelComposition.current!!.openTelemetry) {
Text(text = timeConsumingCalculation(delay))
}

HoneycombInstrumentedComposable("expensive text 2") {
HoneycombInstrumentedComposable("expensive text 2", LocalOtelComposition.current!!.openTelemetry) {
Text(text = timeConsumingCalculation(delay))
}

HoneycombInstrumentedComposable("expensive text 3") {
HoneycombInstrumentedComposable("expensive text 3", LocalOtelComposition.current!!.openTelemetry) {
Text(text = timeConsumingCalculation(delay))
}

HoneycombInstrumentedComposable("nested expensive view") {
HoneycombInstrumentedComposable("nested expensive view", LocalOtelComposition.current!!.openTelemetry) {
NestedExpensiveView(delayMs = delay)
}

HoneycombInstrumentedComposable("expensive text 4") {
HoneycombInstrumentedComposable("expensive text 4", LocalOtelComposition.current!!.openTelemetry) {
Text(text = timeConsumingCalculation(delay))
}
}
Expand All @@ -146,7 +101,7 @@ internal fun ViewInstrumentationPlayground() {
Switch(
checked = enabled,
onCheckedChange = setEnabled,
modifier = Modifier.testTag("slow_render_switch")
modifier = Modifier.testTag("slow_render_switch"),
)
}
if (enabled) {
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ appcompat = "1.7.0"
material = "1.12.0"
activity = "1.9.3"
constraintlayout = "2.1.4"
runtimeAndroid = "1.7.6"

[libraries]
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
Expand Down Expand Up @@ -60,6 +61,7 @@ androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
androidx-runtime-android = { group = "androidx.compose.runtime", name = "runtime-android", version.ref = "runtimeAndroid" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Expand Down

0 comments on commit 7c45b19

Please sign in to comment.