Skip to content

Commit d52ac80

Browse files
committed
Merge remote-tracking branch 'origin/main' into ta/O11Y-606/replay
2 parents 627d5e5 + 38b4a84 commit d52ac80

File tree

7 files changed

+525
-12
lines changed

7 files changed

+525
-12
lines changed

e2e/android/app/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins {
77

88
android {
99
namespace = "com.example.androidobservability"
10-
compileSdk = 35
10+
compileSdk = 36
1111

1212
defaultConfig {
1313
applicationId = "com.example.androidobservability"

e2e/android/app/src/main/java/com/example/androidobservability/MainActivity.kt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Column
1010
import androidx.compose.foundation.layout.Spacer
1111
import androidx.compose.foundation.layout.fillMaxSize
1212
import androidx.compose.foundation.layout.height
13+
import androidx.compose.foundation.layout.imePadding
1314
import androidx.compose.foundation.layout.padding
1415
import androidx.compose.foundation.rememberScrollState
1516
import androidx.compose.foundation.verticalScroll
@@ -35,16 +36,21 @@ class MainActivity : ComponentActivity() {
3536
enableEdgeToEdge()
3637
setContent {
3738
AndroidObservabilityTheme {
38-
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
39+
Scaffold(
40+
modifier = Modifier
41+
.fillMaxSize()
42+
.imePadding()
43+
) { innerPadding ->
3944
var customLogText by remember { mutableStateOf("") }
4045
var customSpanText by remember { mutableStateOf("") }
4146
var customContextKey by remember { mutableStateOf("") }
4247

4348
Column(
4449
modifier = Modifier
50+
.fillMaxSize()
4551
.padding(innerPadding)
46-
.padding(16.dp)
4752
.verticalScroll(rememberScrollState())
53+
.padding(16.dp)
4854
) {
4955
Text(
5056
text = "Hello Telemetry",
@@ -77,6 +83,34 @@ class MainActivity : ComponentActivity() {
7783
) {
7884
Text("Trigger Metric")
7985
}
86+
Button(
87+
onClick = {
88+
viewModel.triggerHistogramMetric()
89+
}
90+
) {
91+
Text("Trigger Histogram Metric")
92+
}
93+
Button(
94+
onClick = {
95+
viewModel.triggerCountMetric()
96+
}
97+
) {
98+
Text("Trigger Count Metric")
99+
}
100+
Button(
101+
onClick = {
102+
viewModel.triggerIncrementalMetric()
103+
}
104+
) {
105+
Text("Trigger Incremental Metric")
106+
}
107+
Button(
108+
onClick = {
109+
viewModel.triggerUpDownCounterMetric()
110+
}
111+
) {
112+
Text("Trigger UpDownCounter Metric")
113+
}
80114
Button(
81115
onClick = {
82116
viewModel.triggerError()

e2e/android/app/src/main/java/com/example/androidobservability/ViewModel.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,23 @@ import java.net.URL
2121
class ViewModel : ViewModel() {
2222

2323
fun triggerMetric() {
24-
LDObserve.recordMetric(Metric("test", 50.0))
24+
LDObserve.recordMetric(Metric("test-gauge", 50.0))
25+
}
26+
27+
fun triggerHistogramMetric() {
28+
LDObserve.recordHistogram(Metric("test-histogram", 15.0))
29+
}
30+
31+
fun triggerCountMetric() {
32+
LDObserve.recordCount(Metric("test-counter", 10.0))
33+
}
34+
35+
fun triggerIncrementalMetric() {
36+
LDObserve.recordIncr(Metric("test-incremental-counter", 12.0))
37+
}
38+
39+
fun triggerUpDownCounterMetric() {
40+
LDObserve.recordUpDownCounter(Metric("test-up-down-counter", 25.0))
2541
}
2642

2743
fun triggerError() {

sdk/@launchdarkly/observability-android/lib/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ dependencies {
2323
implementation("com.launchdarkly:launchdarkly-android-client-sdk:5.9.0")
2424
implementation("com.jakewharton.timber:timber:5.0.1")
2525

26+
// Android
27+
implementation("androidx.activity:activity:1.11.0")
28+
2629
// Coroutines
2730
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
2831
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2")
@@ -74,7 +77,7 @@ tasks.withType<Test> {
7477

7578
android {
7679
namespace = "com.launchdarkly.observability"
77-
compileSdk = 35
80+
compileSdk = 36
7881

7982
buildFeatures {
8083
buildConfig = true

sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/DebugMetricExporter.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@ import io.opentelemetry.sdk.common.CompletableResultCode
55
import io.opentelemetry.sdk.metrics.InstrumentType
66
import io.opentelemetry.sdk.metrics.data.AggregationTemporality
77
import io.opentelemetry.sdk.metrics.data.MetricData
8+
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector
89
import io.opentelemetry.sdk.metrics.export.MetricExporter
910

1011
class DebugMetricExporter(private val logger: LDLogger): MetricExporter {
1112

1213
override fun export(metrics: Collection<MetricData?>): CompletableResultCode? {
1314
for (metric in metrics) {
14-
logger.info(metric.toString())
15+
logger.info("Metric exported: ${metric.toString()}")
1516
}
1617
return CompletableResultCode.ofSuccess()
1718
}
1819

1920
override fun flush(): CompletableResultCode = CompletableResultCode.ofSuccess()
2021
override fun shutdown(): CompletableResultCode = CompletableResultCode.ofSuccess()
21-
override fun getAggregationTemporality(instrumentType: InstrumentType): AggregationTemporality? = AggregationTemporality.CUMULATIVE
22+
override fun getAggregationTemporality(instrumentType: InstrumentType): AggregationTemporality? =
23+
AggregationTemporalitySelector.deltaPreferred().getAggregationTemporality(instrumentType)
2224
}

sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/InstrumentationManager.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder
3535
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessor
3636
import io.opentelemetry.sdk.logs.export.LogRecordExporter
3737
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder
38+
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector
3839
import io.opentelemetry.sdk.metrics.export.MetricExporter
3940
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader
4041
import io.opentelemetry.sdk.resources.Resource
@@ -75,6 +76,7 @@ class InstrumentationManager(
7576
private var spanProcessor: BatchSpanProcessor? = null
7677
private var logProcessor: LogRecordProcessor? = null
7778
private var metricsReader: PeriodicMetricReader? = null
79+
private var launchTimeInstrumentation: LaunchTimeInstrumentation? = null
7880
private val gaugeCache = ConcurrentHashMap<String, DoubleGauge>()
7981
private val counterCache = ConcurrentHashMap<String, LongCounter>()
8082
private val histogramCache = ConcurrentHashMap<String, DoubleHistogram>()
@@ -87,7 +89,7 @@ class InstrumentationManager(
8789
initializeTelemetryInspector()
8890
val otelRumConfig = createOtelRumConfig()
8991

90-
val builder = OpenTelemetryRum.builder(application, otelRumConfig)
92+
val rumBuilder = OpenTelemetryRum.builder(application, otelRumConfig)
9193
.addLoggerProviderCustomizer { sdkLoggerProviderBuilder, _ ->
9294
// TODO: O11Y-627 - need to refactor this so that the disableLogs option is specific to core logging functionality. when logs are disabled, session replay logs should not be blocked
9395
return@addLoggerProviderCustomizer if (options.disableLogs && options.disableErrorTracking) {
@@ -122,10 +124,19 @@ class InstrumentationManager(
122124
}
123125

124126
for (instrumentation in options.instrumentations) {
125-
builder.addInstrumentation(instrumentation)
127+
rumBuilder.addInstrumentation(instrumentation)
126128
}
127129

128-
otelRUM = builder.build()
130+
if (!options.disableMetrics) {
131+
launchTimeInstrumentation = LaunchTimeInstrumentation(
132+
application = application,
133+
metricRecorder = this::recordHistogram
134+
).also {
135+
rumBuilder.addInstrumentation(it)
136+
}
137+
}
138+
139+
otelRUM = rumBuilder.build()
129140
loadSamplingConfigAsync()
130141

131142
otelMeter = otelRUM.openTelemetry.meterProvider.get(INSTRUMENTATION_SCOPE_NAME)
@@ -190,6 +201,7 @@ class InstrumentationManager(
190201
return OtlpHttpMetricExporter.builder()
191202
.setEndpoint(options.otlpEndpoint + METRICS_PATH)
192203
.setHeaders { options.customHeaders }
204+
.setAggregationTemporalitySelector(AggregationTemporalitySelector.deltaPreferred())
193205
.build()
194206
}
195207

@@ -232,7 +244,7 @@ class InstrumentationManager(
232244
private fun createPeriodicMetricReader(metricExporter: MetricExporter): PeriodicMetricReader {
233245
// Configure a periodic reader that pushes metrics every 10 seconds.
234246
return PeriodicMetricReader.builder(metricExporter)
235-
.setInterval(METRICS_EXPORT_INTERVAL_SECONDS, TimeUnit.SECONDS)
247+
.setInterval(METRICS_EXPORT_INTERVAL_MS, TimeUnit.MILLISECONDS)
236248
.build()
237249
}
238250

@@ -280,6 +292,7 @@ class InstrumentationManager(
280292
val counter = counterCache.getOrPut(metric.name) {
281293
otelMeter.counterBuilder(metric.name).build()
282294
}
295+
// It increments the value until the metric is exported, then it’s reset.
283296
counter.add(1, metric.attributes)
284297
}
285298

@@ -382,7 +395,7 @@ class InstrumentationManager(
382395
private const val BATCH_SCHEDULE_DELAY_MS = 1000L
383396
private const val BATCH_EXPORTER_TIMEOUT_MS = 5000L
384397
private const val BATCH_MAX_EXPORT_SIZE = 10
385-
private const val METRICS_EXPORT_INTERVAL_SECONDS = 10L
398+
private const val METRICS_EXPORT_INTERVAL_MS = 10_000L
386399
private const val FLUSH_TIMEOUT_SECONDS = 5L
387400

388401
internal fun createLoggerProcessor(

0 commit comments

Comments
 (0)