From 41d5b50e6b37b62f774e8b9975a829d2b31e8ff9 Mon Sep 17 00:00:00 2001 From: Reza Stallone Asmuruf Date: Wed, 18 Oct 2023 00:24:54 +0700 Subject: [PATCH] Feature/background aware span export (#648) * add SpanFileProvider Signed-off-by: reza stallone * add StartTypeAwareSpanFileProvider Signed-off-by: reza stallone * add function to calculate dir size from /span & /span/background recursively Signed-off-by: reza stallone * change session id to UUID to loose coupling from otel session id Signed-off-by: reza stallone * provide file path for span using SpanFileProvider Signed-off-by: reza stallone * abstract backlog for MemoryBufferingExporter Signed-off-by: reza stallone * implement start type aware BacklogProvider Signed-off-by: reza stallone * implement default SpanFileProvider Signed-off-by: reza stallone * return start type aware BacklogProvider & SpanFileProvider based on isBackgroundTaskReportingDisabled config Signed-off-by: reza stallone * add unit test for StartTypeAwareSpanFileProvider Signed-off-by: reza stallone * add background worker for testing Signed-off-by: reza stallone * move constructor below fields Signed-off-by: reza stallone * move static constant above fields Signed-off-by: reza stallone * rename fillFromBacklog to drain Signed-off-by: reza stallone * reduce SpanFileProvider access level to package level Signed-off-by: reza stallone * rename SpanFileProvider to SpanStorage Signed-off-by: reza stallone * invert if in provideSpanFile Signed-off-by: reza stallone * pass root directory to SpanStorage constructor instead of Application Signed-off-by: reza stallone * remove Application dependency from StartTypeAwareSpanStorageTest.java Signed-off-by: reza stallone * reduce access level BacklogProvider to package level Signed-off-by: reza stallone * move max span in backlog checking back to MemoryBufferingExporter Signed-off-by: reza stallone * rename StartTypeAwareBacklogProvider to StartTypeAwareMemorySpanBuffer Signed-off-by: reza stallone * use Stream to list file recursively Signed-off-by: reza stallone * reword description for StartTypeAwareSpanStorage Signed-off-by: reza stallone * remove unused semicolon Signed-off-by: reza stallone * call forEach directly instead of collect since it's also terminal operator Signed-off-by: reza stallone * remove unused import Signed-off-by: reza stallone * use debug log level instead of information Signed-off-by: reza stallone * move methods close to immediate caller for readability Signed-off-by: reza stallone * remove DemoWorker from manifest declaration Signed-off-by: reza stallone * add javadoc for SpanStorage Signed-off-by: reza stallone * rename backgroundTaskInstrumentationEnabled Signed-off-by: reza stallone * add deffer background instrumentation until foreground Rum builder parameter Signed-off-by: reza stallone * remove max size backlog test since the logic has been moved out of this class Signed-off-by: reza stallone * update unit test StartTypeAwareMemorySpanBufferTest Signed-off-by: reza stallone * remove max size exceed test from DefaultMemorySpanBufferTest Since the logic has been moved back to MemoryBufferingExporter Signed-off-by: reza stallone * apply spotless Signed-off-by: reza stallone * apply spotless to splunk-otel-android project Signed-off-by: reza stallone * downgrade io.opentelemetry.android:instrumentation to 0.1.0-alpha-SNAPSHOTS cannot parse pom 0.2.0-alpha-SNAPSHOTS Signed-off-by: reza stallone * Revert "downgrade io.opentelemetry.android:instrumentation to 0.1.0-alpha-SNAPSHOTS" This reverts commit 1a0a1db6f7053c2a5f2296d7c34878d2645b0f6e. Signed-off-by: reza stallone * fix background span not sent because not checking visibility tracker current screen Signed-off-by: reza stallone * clean up old background directories on new uniqueId created Signed-off-by: reza stallone * apply spotless Signed-off-by: reza stallone * differentiate screen name between previous visible screen & current visible screen Signed-off-by: reza stallone * reword cleanupUnsentBackgroundSpans Signed-off-by: reza stallone * clean background span directories after moving its spans to foreground Signed-off-by: reza stallone * apply javabean convention for variable name in ConfigFlags.java Signed-off-by: reza stallone * rename isSubprocessInstrumentationDisabled to isSubprocessInstrumentationEnabled Signed-off-by: reza stallone * create factory method for StartTypeAwareSpanStorage Signed-off-by: reza stallone * reword file to dir Co-authored-by: jason plumb <75337021+breedx-splk@users.noreply.github.com> Signed-off-by: reza stallone * reword file to dir Co-authored-by: jason plumb <75337021+breedx-splk@users.noreply.github.com> Signed-off-by: reza stallone * apply spotless Signed-off-by: reza stallone * fix visibility checking bug in StartTypeAwareMemorySpanBuffer Signed-off-by: reza stallone * reword disableBackgroundTaskReporting to disableSubprocessInstrumentation Signed-off-by: reza stallone * disableSubprocessInstrumentation in sample app Signed-off-by: reza stallone * apply spotless Signed-off-by: reza stallone * reword reword term backgroundTaskReporting to subprocessInstrumentation Signed-off-by: reza stallone --------- Signed-off-by: reza stallone Co-authored-by: jason plumb <75337021+breedx-splk@users.noreply.github.com> --- sample-app/build.gradle.kts | 1 + sample-app/src/main/AndroidManifest.xml | 4 + .../com/splunk/android/sample/DemoWorker.java | 56 +++++++ .../splunk/android/sample/FirstFragment.java | 5 + .../splunk/android/sample/MainActivity.java | 15 ++ .../android/sample/SampleApplication.java | 3 +- .../sample/SplunkBackgroundService.java | 40 +++++ .../android/sample/WorkManagerHelper.java | 30 ++++ .../src/main/res/layout/fragment_first.xml | 7 + sample-app/src/main/res/values/strings.xml | 1 + .../main/java/com/splunk/rum/ConfigFlags.java | 19 ++- .../splunk/rum/DefaultMemorySpanBuffer.java | 63 ++++++++ .../com/splunk/rum/DefaultSpanStorage.java | 60 ++++++++ .../splunk/rum/DeviceSpanStorageLimiter.java | 18 +-- .../com/splunk/rum/DiskToZipkinExporter.java | 14 +- .../main/java/com/splunk/rum/FileUtils.java | 32 +++- .../splunk/rum/MemoryBufferingExporter.java | 30 ++-- .../java/com/splunk/rum/MemorySpanBuffer.java | 35 +++++ .../java/com/splunk/rum/RumInitializer.java | 58 +++++-- .../main/java/com/splunk/rum/SpanStorage.java | 48 ++++++ .../main/java/com/splunk/rum/SplunkRum.java | 2 +- .../java/com/splunk/rum/SplunkRumBuilder.java | 33 +++- .../rum/StartTypeAwareMemorySpanBuffer.java | 102 +++++++++++++ .../splunk/rum/StartTypeAwareSpanStorage.java | 143 ++++++++++++++++++ ...sDetector.java => SubprocessDetector.java} | 6 +- .../com/splunk/rum/ZipkinToDiskSender.java | 12 +- .../rum/ZipkinWriteToDiskExporterFactory.java | 21 +-- .../rum/DefaultMemorySpanBufferTest.java | 43 ++++++ .../rum/DeviceSpanStorageLimiterTest.java | 33 ++-- .../splunk/rum/DiskToZipkinExporterTest.java | 29 ++-- .../rum/MemoryBufferingExporterTest.java | 73 +++++---- .../com/splunk/rum/RumInitializerTest.java | 13 +- .../java/com/splunk/rum/SplunkRumTest.java | 5 - .../StartTypeAwareMemorySpanBufferTest.java | 94 ++++++++++++ .../rum/StartTypeAwareSpanStorageTest.java | 121 +++++++++++++++ .../splunk/rum/ZipkinToDiskSenderTest.java | 13 +- 36 files changed, 1123 insertions(+), 159 deletions(-) create mode 100644 sample-app/src/main/java/com/splunk/android/sample/DemoWorker.java create mode 100644 sample-app/src/main/java/com/splunk/android/sample/SplunkBackgroundService.java create mode 100644 sample-app/src/main/java/com/splunk/android/sample/WorkManagerHelper.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/DefaultMemorySpanBuffer.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/DefaultSpanStorage.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/MemorySpanBuffer.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/SpanStorage.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/StartTypeAwareMemorySpanBuffer.java create mode 100644 splunk-otel-android/src/main/java/com/splunk/rum/StartTypeAwareSpanStorage.java rename splunk-otel-android/src/main/java/com/splunk/rum/{BackgroundProcessDetector.java => SubprocessDetector.java} (92%) create mode 100644 splunk-otel-android/src/test/java/com/splunk/rum/DefaultMemorySpanBufferTest.java create mode 100644 splunk-otel-android/src/test/java/com/splunk/rum/StartTypeAwareMemorySpanBufferTest.java create mode 100644 splunk-otel-android/src/test/java/com/splunk/rum/StartTypeAwareSpanStorageTest.java diff --git a/sample-app/build.gradle.kts b/sample-app/build.gradle.kts index b194947d..68d4d86d 100644 --- a/sample-app/build.gradle.kts +++ b/sample-app/build.gradle.kts @@ -71,6 +71,7 @@ dependencies { implementation(project(":splunk-otel-android")) implementation(project(":splunk-otel-android-volley")) implementation("com.android.volley:volley:1.2.1") + implementation("androidx.work:work-runtime:2.8.1") implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api") implementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv") diff --git a/sample-app/src/main/AndroidManifest.xml b/sample-app/src/main/AndroidManifest.xml index fa4c5951..bec0ab4c 100644 --- a/sample-app/src/main/AndroidManifest.xml +++ b/sample-app/src/main/AndroidManifest.xml @@ -25,6 +25,10 @@ + \ No newline at end of file diff --git a/sample-app/src/main/java/com/splunk/android/sample/DemoWorker.java b/sample-app/src/main/java/com/splunk/android/sample/DemoWorker.java new file mode 100644 index 00000000..33691503 --- /dev/null +++ b/sample-app/src/main/java/com/splunk/android/sample/DemoWorker.java @@ -0,0 +1,56 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.android.sample; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; +import com.splunk.rum.SplunkRum; +import io.opentelemetry.api.common.Attributes; + +public class DemoWorker extends Worker { + + private Context context; + public static final String TAG = "SplunkRum"; + + public DemoWorker(@NonNull Context context, @NonNull WorkerParameters params) { + super(context, params); + this.context = context; + } + + @NonNull + @Override + public Result doWork() { + try { + SplunkRum.getInstance().addRumEvent("DemoWorker is doing work", Attributes.empty()); + Log.d(TAG, "DemoWorker Starting background Service"); + startBackgroundService(); + return Result.success(); + } catch (Exception e) { + return Result.failure(); + } + } + + private void startBackgroundService() { + Intent serviceIntent = new Intent(context, SplunkBackgroundService.class); + Log.d(TAG, "Starting background Service"); + context.startService(serviceIntent); + } +} diff --git a/sample-app/src/main/java/com/splunk/android/sample/FirstFragment.java b/sample-app/src/main/java/com/splunk/android/sample/FirstFragment.java index 135c45e2..0931f939 100644 --- a/sample-app/src/main/java/com/splunk/android/sample/FirstFragment.java +++ b/sample-app/src/main/java/com/splunk/android/sample/FirstFragment.java @@ -110,6 +110,11 @@ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { }); sessionId.postValue(splunkRum.getRumSessionId()); + + binding.workManager.setOnClickListener( + v -> { + WorkManagerHelper.startWorkManager(this.getContext()); + }); } private void multiThreadCrashing() { diff --git a/sample-app/src/main/java/com/splunk/android/sample/MainActivity.java b/sample-app/src/main/java/com/splunk/android/sample/MainActivity.java index 71d3ebf1..00c9367a 100644 --- a/sample-app/src/main/java/com/splunk/android/sample/MainActivity.java +++ b/sample-app/src/main/java/com/splunk/android/sample/MainActivity.java @@ -38,6 +38,9 @@ import androidx.navigation.Navigation; import androidx.navigation.ui.AppBarConfiguration; import androidx.navigation.ui.NavigationUI; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; import com.splunk.android.sample.databinding.ActivityMainBinding; import com.splunk.rum.SplunkRum; import io.opentelemetry.android.instrumentation.RumScreenName; @@ -74,6 +77,18 @@ protected void onCreate(Bundle savedInstanceState) { view -> { new MailDialogFragment(this).show(getSupportFragmentManager(), "Mail"); }); + setUpPeriodicWorker(); + } + + private void setUpPeriodicWorker() { + PeriodicWorkRequest workRequest = + new PeriodicWorkRequest.Builder(DemoWorker.class, 15, TimeUnit.MINUTES).build(); + + WorkManager.getInstance(this) + .enqueueUniquePeriodicWork( + "backgroundsplunk", + ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, + workRequest); } @Override diff --git a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java index 9f5ed8ba..ff1f4344 100644 --- a/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java +++ b/sample-app/src/main/java/com/splunk/android/sample/SampleApplication.java @@ -42,7 +42,8 @@ public void onCreate() { .setRumAccessToken(getResources().getString(R.string.rum_access_token)) .enableDebug() .enableDiskBuffering() - .disableBackgroundTaskReporting(BuildConfig.APPLICATION_ID) + .disableSubprocessInstrumentation(BuildConfig.APPLICATION_ID) + .enableBackgroundInstrumentationDeferredUntilForeground() .setSlowRenderingDetectionPollInterval(Duration.ofMillis(1000)) .setDeploymentEnvironment("demo") .limitDiskUsageMegabytes(1) diff --git a/sample-app/src/main/java/com/splunk/android/sample/SplunkBackgroundService.java b/sample-app/src/main/java/com/splunk/android/sample/SplunkBackgroundService.java new file mode 100644 index 00000000..f824f9bf --- /dev/null +++ b/sample-app/src/main/java/com/splunk/android/sample/SplunkBackgroundService.java @@ -0,0 +1,40 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.android.sample; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +public class SplunkBackgroundService extends Service { + public static final String TAG = "SplunkRum"; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (intent != null) { + Log.d(TAG, "Service started on different thread"); + } + stopSelf(); + return START_STICKY; + } +} diff --git a/sample-app/src/main/java/com/splunk/android/sample/WorkManagerHelper.java b/sample-app/src/main/java/com/splunk/android/sample/WorkManagerHelper.java new file mode 100644 index 00000000..75f903aa --- /dev/null +++ b/sample-app/src/main/java/com/splunk/android/sample/WorkManagerHelper.java @@ -0,0 +1,30 @@ +/* + * Copyright Splunk Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.splunk.android.sample; + +import android.content.Context; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; + +public class WorkManagerHelper { + + public static void startWorkManager(Context context) { + WorkRequest demoWorkRequest = new OneTimeWorkRequest.Builder(DemoWorker.class).build(); + WorkManager.getInstance(context).enqueue(demoWorkRequest); + } +} diff --git a/sample-app/src/main/res/layout/fragment_first.xml b/sample-app/src/main/res/layout/fragment_first.xml index 1ed1e86e..af78e301 100644 --- a/sample-app/src/main/res/layout/fragment_first.xml +++ b/sample-app/src/main/res/layout/fragment_first.xml @@ -77,6 +77,13 @@ android:layout_marginTop="8dp" android:text="@string/volley_client" /> +