Skip to content

Commit db67867

Browse files
authored
feat: Cache OpenTelemetry metric instruments (#256)
## Summary This change introduces caching for OpenTelemetry metric instruments (Gauge, Counter, Histogram, UpDownCounter) within the `InstrumentationManager`. Previously, a new instrument was built for every metric recording call (`recordMetric`, `recordCount`, etc.), which is inefficient. Now, instruments are created once per metric name and reused for subsequent calls, improving performance. ## How did you test this change? -- ## Are there any deployment considerations? No <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Caches OTEL metric instruments in `InstrumentationManager` and reuses them per metric name instead of rebuilding on each call. > > - **Metrics caching in `sdk/@launchdarkly/observability-android/.../InstrumentationManager.kt`**: > - Add caches for instruments: `gaugeCache`, `counterCache`, `histogramCache`, `upDownCounterCache` using `ConcurrentHashMap`. > - Import instrument types (`DoubleGauge`, `LongCounter`, `DoubleHistogram`, `LongUpDownCounter`) and `ConcurrentHashMap`. > - Update `recordMetric`, `recordCount`, `recordIncr`, `recordHistogram`, `recordUpDownCounter` to `getOrPut` and reuse instruments by name. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 6888028. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 9a9ebe2 commit db67867

File tree

1 file changed

+29
-11
lines changed

1 file changed

+29
-11
lines changed

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

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import io.opentelemetry.android.session.SessionConfig
1818
import io.opentelemetry.api.common.Attributes
1919
import io.opentelemetry.api.logs.Logger
2020
import io.opentelemetry.api.logs.Severity
21+
import io.opentelemetry.api.metrics.DoubleGauge
22+
import io.opentelemetry.api.metrics.DoubleHistogram
23+
import io.opentelemetry.api.metrics.LongCounter
24+
import io.opentelemetry.api.metrics.LongUpDownCounter
2125
import io.opentelemetry.api.metrics.Meter
2226
import io.opentelemetry.api.trace.Span
2327
import io.opentelemetry.api.trace.Tracer
@@ -43,6 +47,7 @@ import kotlinx.coroutines.CoroutineScope
4347
import kotlinx.coroutines.Dispatchers
4448
import kotlinx.coroutines.SupervisorJob
4549
import kotlinx.coroutines.launch
50+
import java.util.concurrent.ConcurrentHashMap
4651
import java.util.concurrent.TimeUnit
4752

4853
/**
@@ -87,10 +92,13 @@ class InstrumentationManager(
8792
private var inMemoryLogExporter: InMemoryLogRecordExporter? = null
8893
private var inMemoryMetricExporter: InMemoryMetricExporter? = null
8994
private var telemetryInspector: TelemetryInspector? = null
90-
9195
private var spanProcessor: BatchSpanProcessor? = null
9296
private var logProcessor: BatchLogRecordProcessor? = null
9397
private var metricsReader: PeriodicMetricReader? = null
98+
private val gaugeCache = ConcurrentHashMap<String, DoubleGauge>()
99+
private val counterCache = ConcurrentHashMap<String, LongCounter>()
100+
private val histogramCache = ConcurrentHashMap<String, DoubleHistogram>()
101+
private val upDownCounterCache = ConcurrentHashMap<String, LongUpDownCounter>()
94102

95103
//TODO: Evaluate if this class should have a close/shutdown method to close this scope
96104
private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
@@ -275,29 +283,39 @@ class InstrumentationManager(
275283
}
276284

277285
fun recordMetric(metric: Metric) {
278-
otelMeter.gaugeBuilder(metric.name).build()
279-
.set(metric.value, metric.attributes)
286+
val gauge = gaugeCache.getOrPut(metric.name) {
287+
otelMeter.gaugeBuilder(metric.name).build()
288+
}
289+
gauge.set(metric.value, metric.attributes)
280290
}
281291

282292
fun recordCount(metric: Metric) {
283293
// TODO: handle double casting to long better
284-
otelMeter.counterBuilder(metric.name).build()
285-
.add(metric.value.toLong(), metric.attributes)
294+
val counter = counterCache.getOrPut(metric.name) {
295+
otelMeter.counterBuilder(metric.name).build()
296+
}
297+
counter.add(metric.value.toLong(), metric.attributes)
286298
}
287299

288300
fun recordIncr(metric: Metric) {
289-
otelMeter.counterBuilder(metric.name).build()
290-
.add(1, metric.attributes)
301+
val counter = counterCache.getOrPut(metric.name) {
302+
otelMeter.counterBuilder(metric.name).build()
303+
}
304+
counter.add(1, metric.attributes)
291305
}
292306

293307
fun recordHistogram(metric: Metric) {
294-
otelMeter.histogramBuilder(metric.name).build()
295-
.record(metric.value, metric.attributes)
308+
val histogram = histogramCache.getOrPut(metric.name) {
309+
otelMeter.histogramBuilder(metric.name).build()
310+
}
311+
histogram.record(metric.value, metric.attributes)
296312
}
297313

298314
fun recordUpDownCounter(metric: Metric) {
299-
otelMeter.upDownCounterBuilder(metric.name).build()
300-
.add(metric.value.toLong(), metric.attributes)
315+
val upDownCounter = upDownCounterCache.getOrPut(metric.name) {
316+
otelMeter.upDownCounterBuilder(metric.name).build()
317+
}
318+
upDownCounter.add(metric.value.toLong(), metric.attributes)
301319
}
302320

303321
fun recordLog(message: String, severity: Severity, attributes: Attributes) {

0 commit comments

Comments
 (0)