Skip to content

Commit 67159df

Browse files
Merge branch 'main' of github.com:launchdarkly/observability-sdk into O11Y-529-Support-a-custom-mask-class-for-session-replay-plugin
2 parents 35c89a0 + 3548b42 commit 67159df

File tree

29 files changed

+1016
-204
lines changed

29 files changed

+1016
-204
lines changed

.release-please-manifest.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"go": "0.3.0",
33
"sdk/@launchdarkly/observability": "0.4.2",
4-
"sdk/@launchdarkly/observability-android": "0.7.0",
5-
"sdk/@launchdarkly/observability-dotnet": "0.2.0",
4+
"sdk/@launchdarkly/observability-android": "0.8.0",
5+
"sdk/@launchdarkly/observability-dotnet": "0.3.0",
66
"sdk/@launchdarkly/observability-node": "0.3.0",
77
"sdk/@launchdarkly/observability-python": "0.1.1",
88
"sdk/@launchdarkly/observability-react-native": "0.6.0",

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,21 @@ open class BaseApplication : Application() {
2323
const val LAUNCHDARKLY_MOBILE_KEY = "MOBILE_KEY_GOES_HERE"
2424
}
2525

26-
var telemetryInspector: TelemetryInspector? = null
27-
28-
override fun onCreate() {
29-
super.onCreate()
26+
var pluginOptions = Options(
27+
resourceAttributes = Attributes.of(
28+
AttributeKey.stringKey("example"), "value"
29+
),
30+
debug = true,
31+
logAdapter = LDAndroidLogging.adapter(),
32+
)
3033

31-
val testUrl = System.getProperty("e2e_test_base_url")
32-
val pluginOptions = Options(
33-
resourceAttributes = Attributes.of(
34-
AttributeKey.stringKey("example"), "value"
35-
),
36-
debug = true,
37-
logAdapter = LDAndroidLogging.adapter(),
38-
)
34+
var telemetryInspector: TelemetryInspector? = null
35+
var testUrl: String? = null
3936

37+
open fun realInit() {
4038
val observabilityPlugin = Observability(
4139
application = this@BaseApplication,
42-
options = if (testUrl != null) pluginOptions.copy(backendUrl = testUrl) else pluginOptions
40+
options = testUrl?.let { pluginOptions.copy(backendUrl = it, otlpEndpoint = it) } ?: pluginOptions
4341
)
4442

4543
// Set LAUNCHDARKLY_MOBILE_KEY to your LaunchDarkly mobile key found on the LaunchDarkly
@@ -62,7 +60,11 @@ open class BaseApplication : Application() {
6260
.build()
6361

6462
LDClient.init(this@BaseApplication, ldConfig, context)
65-
6663
telemetryInspector = observabilityPlugin.getTelemetryInspector()
6764
}
65+
66+
override fun onCreate() {
67+
super.onCreate()
68+
realInit()
69+
}
6870
}

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ class MainActivity : ComponentActivity() {
3838
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
3939
var customLogText by remember { mutableStateOf("") }
4040
var customSpanText by remember { mutableStateOf("") }
41+
var customContextKey by remember { mutableStateOf("") }
4142

4243
Column(
4344
modifier = Modifier
4445
.padding(innerPadding)
4546
.padding(16.dp)
4647
.verticalScroll(rememberScrollState())
4748
) {
48-
4949
Text(
5050
text = "Hello Telemetry",
5151
modifier = Modifier.padding(bottom = 16.dp)
@@ -137,6 +137,23 @@ class MainActivity : ComponentActivity() {
137137
) {
138138
Text("Send custom span")
139139
}
140+
141+
Spacer(modifier = Modifier.height(16.dp))
142+
143+
OutlinedTextField(
144+
value = customContextKey,
145+
onValueChange = { customContextKey = it },
146+
label = { Text("LD context key") },
147+
modifier = Modifier.padding(8.dp)
148+
)
149+
Button(
150+
onClick = {
151+
viewModel.identifyLDContext(customContextKey)
152+
},
153+
modifier = Modifier.padding(8.dp)
154+
) {
155+
Text("Identify LD Context")
156+
}
140157
}
141158
}
142159
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.launchdarkly.observability.interfaces.Metric
66
import com.launchdarkly.observability.sdk.LDObserve
7+
import com.launchdarkly.sdk.ContextKind
8+
import com.launchdarkly.sdk.LDContext
9+
import com.launchdarkly.sdk.android.LDClient
710
import io.opentelemetry.api.common.AttributeKey
811
import io.opentelemetry.api.common.Attributes
912
import io.opentelemetry.api.logs.Severity
@@ -94,6 +97,14 @@ class ViewModel : ViewModel() {
9497
}
9598
}
9699

100+
fun identifyLDContext(contextKey: String = "test-context-key") {
101+
val context = LDContext.builder(ContextKind.DEFAULT, contextKey)
102+
.name("test-context-name")
103+
.build()
104+
105+
LDClient.get().identify(context)
106+
}
107+
97108
private fun sendOkHttpRequest() {
98109
// Create HTTP client
99110
val client = OkHttpClient()
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package com.example.androidobservability
2+
3+
import android.app.Application
4+
import androidx.test.core.app.ApplicationProvider
5+
import com.example.androidobservability.TestUtils.waitForTelemetryData
6+
import com.launchdarkly.observability.interfaces.Metric
7+
import com.launchdarkly.observability.sdk.LDObserve
8+
import io.opentelemetry.api.common.AttributeKey
9+
import io.opentelemetry.api.common.Attributes
10+
import io.opentelemetry.api.logs.Severity
11+
import com.example.androidobservability.TestUtils.TelemetryType
12+
import junit.framework.TestCase.assertEquals
13+
import junit.framework.TestCase.assertFalse
14+
import junit.framework.TestCase.assertNotNull
15+
import junit.framework.TestCase.assertNull
16+
import junit.framework.TestCase.assertTrue
17+
import org.junit.Test
18+
import org.junit.runner.RunWith
19+
import org.robolectric.RobolectricTestRunner
20+
import org.robolectric.annotation.Config
21+
import java.util.concurrent.TimeUnit
22+
23+
@RunWith(RobolectricTestRunner::class)
24+
@Config(application = TestApplication::class)
25+
class DisablingConfigOptionsE2ETest {
26+
27+
private val application = ApplicationProvider.getApplicationContext<Application>() as TestApplication
28+
29+
@Test
30+
fun `Logs should not be exported when disableLogs is set to true`() {
31+
application.pluginOptions = application.pluginOptions.copy(disableLogs = true)
32+
application.initForTest()
33+
val logsUrl = "http://localhost:${application.mockWebServer?.port}/v1/logs"
34+
35+
triggerTestLog()
36+
LDObserve.flush()
37+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.LOGS)
38+
39+
assertNull(application.telemetryInspector?.logExporter)
40+
assertFalse(requestsContainsUrl(logsUrl))
41+
}
42+
43+
@Test
44+
fun `Logs should be exported when disableLogs is set to false`() {
45+
application.pluginOptions = application.pluginOptions.copy(disableLogs = false)
46+
application.initForTest()
47+
val logsUrl = "http://localhost:${application.mockWebServer?.port}/v1/logs"
48+
49+
triggerTestLog()
50+
LDObserve.flush()
51+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.LOGS)
52+
53+
assertNotNull(application.telemetryInspector?.logExporter)
54+
assertTrue(requestsContainsUrl(logsUrl))
55+
}
56+
57+
@Test
58+
fun `Spans should NOT be exported when disableTraces is set to true`() {
59+
application.pluginOptions = application.pluginOptions.copy(disableTraces = true)
60+
application.initForTest()
61+
val tracesUrl = "http://localhost:${application.mockWebServer?.port}/v1/traces"
62+
63+
triggerTestSpan()
64+
LDObserve.flush()
65+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.SPANS)
66+
67+
assertNull(application.telemetryInspector?.spanExporter)
68+
assertFalse(requestsContainsUrl(tracesUrl))
69+
}
70+
71+
@Test
72+
fun `Spans should be exported when disableTraces is set to false`() {
73+
application.pluginOptions = application.pluginOptions.copy(disableTraces = false)
74+
application.initForTest()
75+
val tracesUrl = "http://localhost:${application.mockWebServer?.port}/v1/traces"
76+
77+
triggerTestSpan()
78+
LDObserve.flush()
79+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.SPANS)
80+
81+
assertNotNull(application.telemetryInspector?.spanExporter)
82+
assertTrue(requestsContainsUrl(tracesUrl))
83+
}
84+
85+
@Test
86+
fun `Metrics should NOT be exported when disableMetrics is set to true`() {
87+
application.pluginOptions = application.pluginOptions.copy(disableMetrics = true)
88+
application.initForTest()
89+
val metricsUrl = "http://localhost:${application.mockWebServer?.port}/v1/metrics"
90+
91+
triggerTestMetric()
92+
LDObserve.flush()
93+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.METRICS)
94+
95+
assertNull(application.telemetryInspector?.metricExporter)
96+
assertFalse(requestsContainsUrl(metricsUrl))
97+
}
98+
99+
@Test
100+
fun `Metrics should be exported when disableMetrics is set to false`() {
101+
application.pluginOptions = application.pluginOptions.copy(disableMetrics = false)
102+
application.initForTest()
103+
val metricsUrl = "http://localhost:${application.mockWebServer?.port}/v1/metrics"
104+
105+
triggerTestMetric()
106+
LDObserve.flush()
107+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.METRICS)
108+
109+
assertNotNull(application.telemetryInspector?.metricExporter)
110+
assertTrue(requestsContainsUrl(metricsUrl))
111+
}
112+
113+
@Test
114+
fun `Errors should NOT be exported when disableErrorTracking is set to true`() {
115+
application.pluginOptions = application.pluginOptions.copy(disableErrorTracking = true)
116+
application.initForTest()
117+
val tracesUrl = "http://localhost:${application.mockWebServer?.port}/v1/traces"
118+
119+
triggerError()
120+
LDObserve.flush()
121+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.SPANS)
122+
123+
val spansExported = application.telemetryInspector?.spanExporter?.finishedSpanItems
124+
125+
assertFalse(requestsContainsUrl(tracesUrl))
126+
assertEquals(0, spansExported?.size)
127+
}
128+
129+
@Test
130+
fun `Errors should be exported when disableErrorTracking is set to false`() {
131+
application.pluginOptions = application.pluginOptions.copy(disableErrorTracking = false)
132+
application.initForTest()
133+
val tracesUrl = "http://localhost:${application.mockWebServer?.port}/v1/traces"
134+
135+
triggerError()
136+
LDObserve.flush()
137+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.SPANS)
138+
139+
val spansExported = application.telemetryInspector?.spanExporter?.finishedSpanItems
140+
141+
assertTrue(requestsContainsUrl(tracesUrl))
142+
assertEquals("highlight.error", spansExported?.get(0)?.name)
143+
assertEquals(
144+
"Test error",
145+
spansExported?.get(0)?.events?.get(0)?.attributes?.get(AttributeKey.stringKey("exception.message"))
146+
)
147+
}
148+
149+
private fun requestsContainsUrl(url: String): Boolean {
150+
while (true) {
151+
val request = application.mockWebServer?.takeRequest(100, TimeUnit.MILLISECONDS)
152+
if (request == null) return false
153+
if (request.requestUrl.toString() == url) return true
154+
}
155+
}
156+
157+
private fun triggerTestLog() {
158+
LDObserve.recordLog(
159+
message = "test-log",
160+
severity = Severity.INFO,
161+
attributes = Attributes.empty()
162+
)
163+
}
164+
165+
private fun triggerTestSpan() {
166+
val span0 = LDObserve.startSpan(
167+
name = "test-span",
168+
attributes = Attributes.empty()
169+
)
170+
span0.end()
171+
}
172+
173+
private fun triggerError() {
174+
LDObserve.recordError(
175+
Error("Test error"),
176+
Attributes.of(AttributeKey.stringKey("test"), "crash_test")
177+
)
178+
}
179+
180+
private fun triggerTestMetric() {
181+
LDObserve.recordMetric(Metric("test", 50.0))
182+
}
183+
}

e2e/android/app/src/test/java/com/example/androidobservability/SamplingE2ETest.kt

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ package com.example.androidobservability
22

33
import android.app.Application
44
import androidx.test.core.app.ApplicationProvider
5+
import com.example.androidobservability.TestUtils.TelemetryType
6+
import com.example.androidobservability.TestUtils.waitForTelemetryData
57
import com.launchdarkly.observability.client.TelemetryInspector
68
import com.launchdarkly.observability.sdk.LDObserve
7-
import com.launchdarkly.sdk.android.LDClient
89
import io.opentelemetry.api.common.AttributeKey
910
import io.opentelemetry.api.common.Attributes
1011
import io.opentelemetry.api.logs.Severity
1112
import junit.framework.TestCase.assertEquals
12-
import org.junit.After
1313
import org.junit.Before
1414
import org.junit.Test
1515
import org.junit.runner.RunWith
@@ -39,24 +39,20 @@ import org.robolectric.annotation.Config
3939
@Config(application = TestApplication::class)
4040
class SamplingE2ETest {
4141

42+
private val application = ApplicationProvider.getApplicationContext<Application>() as TestApplication
4243
private var telemetryInspector: TelemetryInspector? = null
4344

4445
@Before
4546
fun setUp() {
46-
val application = ApplicationProvider.getApplicationContext<Application>() as TestApplication
47+
application.initForTest()
4748
telemetryInspector = application.telemetryInspector
4849
}
4950

50-
@After
51-
fun tearDown(){
52-
LDClient.get().close()
53-
}
54-
5551
@Test
5652
fun `should avoid exporting logs matching sampling configuration for logs`() {
5753
triggerLogs()
5854
telemetryInspector?.logExporter?.flush()
59-
Thread.sleep(6000)
55+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.LOGS)
6056

6157
val logsExported = telemetryInspector?.logExporter?.finishedLogRecordItems?.map {
6258
it.bodyValue?.value.toString()
@@ -72,7 +68,7 @@ class SamplingE2ETest {
7268
fun `should avoid exporting spans matching sampling configuration for spans`() {
7369
triggerSpans()
7470
telemetryInspector?.spanExporter?.flush()
75-
Thread.sleep(6000)
71+
waitForTelemetryData(telemetryInspector = application.telemetryInspector, telemetryType = TelemetryType.SPANS)
7672

7773
val spansExported = telemetryInspector?.spanExporter?.finishedSpanItems?.map {
7874
it.name

0 commit comments

Comments
 (0)