From ae96974929f820efa6a853a8da2f4c0ff1882c77 Mon Sep 17 00:00:00 2001 From: Sergio Villar Senin Date: Fri, 31 Jan 2025 13:36:28 +0100 Subject: [PATCH 1/3] [OpenTelemetry] Setup dependencies and required sw versions This is a preparation commit for the integration of the opentelemetry-android library into Wolvic that will allow us to know more about users actions and their usage of the different features (obviously with their prior consent). The OpenTelemetry android library requires at least a compilesdk 35 and kotlin 2.1 so let's bump them both. Also we need to enable desugaring for the library to work properly with newer versions of Android. We use a different approach to include new dependencies. Instead of using the custom versions.gradle file, we switched to the more modern library catalog declarations. --- app/build.gradle | 10 ++++++++++ gradle/libs.versions.toml | 13 +++++++++++++ versions.gradle | 6 +++--- 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/app/build.gradle b/app/build.gradle index b74d955a05..ef55ea4c96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -160,6 +160,7 @@ android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 + coreLibraryDesugaringEnabled true } packagingOptions { @@ -789,6 +790,15 @@ dependencies { // openwnn (bundled because it is not published outside of JCenter, which is deprecated) implementation fileTree(dir: "${project.rootDir}/libs/openwnn/", include: ['*.aar']) + + // OpenTelemetry + implementation libs.opentelemetry.android.core + implementation libs.opentelemetry.android.instrumentation.activity + implementation libs.opentelemetry.android.instrumentation.anr + implementation libs.opentelemetry.android.instrumentation.crash + implementation libs.opentelemetry.android.instrumentation.sessions + implementation libs.opentelemetry.exporter.logging + coreLibraryDesugaring libs.desugar.jdk.libs } if (findProject(':servo')) { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000000..abfef0549b --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +[versions] +desugar_jdk_libs = "2.1.4" +opentelemetryApi = "1.46.0" +opentelemetryAndroid = "0.9.1-alpha" + +[libraries] +desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" } +opentelemetry-exporter-logging = { module = "io.opentelemetry:opentelemetry-exporter-logging", version.ref = "opentelemetryApi" } +opentelemetry-android-core = { module = "io.opentelemetry.android:core", version.ref = "opentelemetryAndroid" } +opentelemetry-android-instrumentation-activity = { module = "io.opentelemetry.android:instrumentation-activity", version.ref = "opentelemetryAndroid" } +opentelemetry-android-instrumentation-anr = { module = "io.opentelemetry.android:instrumentation-anr", version.ref = "opentelemetryAndroid" } +opentelemetry-android-instrumentation-crash = { module = "io.opentelemetry.android:instrumentation-crash", version.ref = "opentelemetryAndroid" } +opentelemetry-android-instrumentation-sessions = { module = "io.opentelemetry.android:instrumentation-sessions", version.ref = "opentelemetryAndroid" } diff --git a/versions.gradle b/versions.gradle index 7d9f33a913..3b8a02d11f 100644 --- a/versions.gradle +++ b/versions.gradle @@ -46,7 +46,7 @@ versions.atsl_rules = "1.5.0" versions.atsl_junit = "1.1.5" versions.espresso = "3.5.1" versions.android_gradle_plugin = '8.7.2' -versions.kotlin = "1.9.21" +versions.kotlin = "2.1.0" versions.kotlin_coroutines = "1.7.3" versions.snakeyaml = "2.0" versions.gson = "2.10.1" @@ -191,8 +191,8 @@ build_versions.target_sdk_wave = 31 build_versions.target_sdk_aosp = 29 build_versions.target_sdk_spaces = 29 build_versions.target_sdk_visionglass = 33 -build_versions.compile_sdk = 34 -build_versions.build_tools = "34.0.0" +build_versions.compile_sdk = 35 +build_versions.build_tools = "35.0.0" ext.build_versions = build_versions ext.deps = deps From 854a13d0c858f31ddc0a191c3c198f0e0e59e5c8 Mon Sep 17 00:00:00 2001 From: Sergio Villar Senin Date: Wed, 8 Jan 2025 17:38:21 +0100 Subject: [PATCH 2/3] [OpenTelemetry] Inital local only implementation This is an initial implementation of the ITelemetry interface using OpenTelemetry, in particular, the opentelemetry-android library. Using this new library forces us to bump the required kotlin version and the project compilesdk version. So far the telemetry data is logged only to the logcat and not sent to any server. In order to avoid too many writes (and in the future to batch requests to the servers) it uses on disk buffering. --- .../com/igalia/wolvic/VRBrowserActivity.java | 6 ++ .../igalia/wolvic/VRBrowserApplication.java | 2 +- .../wolvic/telemetry/OpenTelemetry.java | 91 +++++++++++++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java diff --git a/app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java b/app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java index 96e0b5c4c0..2186429e30 100644 --- a/app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java +++ b/app/src/common/shared/com/igalia/wolvic/VRBrowserActivity.java @@ -65,6 +65,7 @@ import com.igalia.wolvic.search.SearchEngineWrapper; import com.igalia.wolvic.speech.SpeechRecognizer; import com.igalia.wolvic.speech.SpeechServices; +import com.igalia.wolvic.telemetry.OpenTelemetry; import com.igalia.wolvic.telemetry.TelemetryService; import com.igalia.wolvic.ui.OffscreenDisplay; import com.igalia.wolvic.ui.adapters.Language; @@ -284,6 +285,11 @@ protected void onCreate(Bundle savedInstanceState) { SettingsStore.getInstance(getBaseContext()).setPid(Process.myPid()); ((VRBrowserApplication)getApplication()).onActivityCreate(this); + + if (!DeviceType.isHVRBuild() && SettingsStore.getInstance(getBaseContext()).isTelemetryEnabled()) { + TelemetryService.setService(new OpenTelemetry(getApplication())); + } + // Fix for infinite restart on startup crashes. long count = SettingsStore.getInstance(getBaseContext()).getCrashRestartCount(); boolean cancelRestart = count > CrashReporterService.MAX_RESTART_COUNT; diff --git a/app/src/common/shared/com/igalia/wolvic/VRBrowserApplication.java b/app/src/common/shared/com/igalia/wolvic/VRBrowserApplication.java index c21a4d658a..623080ab3c 100644 --- a/app/src/common/shared/com/igalia/wolvic/VRBrowserApplication.java +++ b/app/src/common/shared/com/igalia/wolvic/VRBrowserApplication.java @@ -53,8 +53,8 @@ public class VRBrowserApplication extends Application implements AppServicesProv protected void onActivityCreate(@NonNull Context activityContext) { onConfigurationChanged(activityContext.getResources().getConfiguration()); - TelemetryService.init(activityContext); mAppExecutors = new AppExecutors(); + TelemetryService.init(activityContext); mConnectivityManager = new ConnectivityReceiver(activityContext); mConnectivityManager.init(); mPlaces = new Places(activityContext); diff --git a/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java b/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java new file mode 100644 index 0000000000..022933ac0d --- /dev/null +++ b/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java @@ -0,0 +1,91 @@ +package com.igalia.wolvic.telemetry; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import android.app.Application; +import android.os.Bundle; + +import com.igalia.wolvic.BuildConfig; +import com.igalia.wolvic.VRBrowserApplication; + +import java.util.concurrent.Executor; + +import io.opentelemetry.android.OpenTelemetryRum; +import io.opentelemetry.android.OpenTelemetryRumBuilder; +import io.opentelemetry.android.config.OtelRumConfig; +import io.opentelemetry.android.features.diskbuffering.DiskBufferingConfiguration; +import io.opentelemetry.android.instrumentation.activity.ActivityLifecycleInstrumentation; +import io.opentelemetry.android.instrumentation.sessions.SessionInstrumentation; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.exporter.logging.LoggingSpanExporter; + +public class OpenTelemetry implements ITelemetry { + private final Application application; + private OpenTelemetryRum rum; + private OpenTelemetryRumBuilder rumBuilder; + private final String instrumentationScopeName = BuildConfig.APPLICATION_ID; + private final String instrumentationScopeVersion = "1.0.0"; + private final Executor diskIOExecutor; + + public OpenTelemetry(Application app) { + application = app; + diskIOExecutor = ((VRBrowserApplication) application).getExecutors().diskIO(); + } + + private void initializeOpenTelemetryAndroid() { + DiskBufferingConfiguration diskBufferingConfiguration = DiskBufferingConfiguration.builder() + .setEnabled(true) + .setMaxCacheSize(10 * 1024 * 1024) + .setMaxFileAgeForWriteMillis(1000 * 5) + .build(); + OtelRumConfig config = new OtelRumConfig() + .setDiskBufferingConfiguration(diskBufferingConfiguration); + rumBuilder = OpenTelemetryRum.builder(application, config) + .addSpanExporterCustomizer(exporter -> LoggingSpanExporter.create()) + .addInstrumentation(new SessionInstrumentation()) + .addInstrumentation(new ActivityLifecycleInstrumentation()); + try { + rum = rumBuilder.build(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void runOnDiskIO(Runnable runnable) { diskIOExecutor.execute(runnable); } + + @Override + public void start() { + assert rum == null; + initializeOpenTelemetryAndroid(); + } + + @Override + public void stop() { + rum = null; + rumBuilder = null; + } + + @Override + public void customEvent(String name) { + assert rum != null; + runOnDiskIO(() -> { + rum.getOpenTelemetry().getTracer(instrumentationScopeName, instrumentationScopeVersion) + .spanBuilder(name) + .startSpan() + .end(); + }); + } + + @Override + public void customEvent(String name, Bundle bundle) { + assert rum != null; + runOnDiskIO(() -> { + SpanBuilder spanBuilder = rum.getOpenTelemetry().getTracer(instrumentationScopeName, instrumentationScopeVersion) + .spanBuilder(name); + for (String key : bundle.keySet()) { + spanBuilder.setAttribute(key, bundle.get(key).toString()); + } + spanBuilder.startSpan().end(); + }); + } +} \ No newline at end of file From c0443266b7aadfae42a004562835b8e77631da2f Mon Sep 17 00:00:00 2001 From: Sergio Villar Senin Date: Mon, 17 Feb 2025 14:49:37 +0100 Subject: [PATCH 3/3] Renames --- .../wolvic/telemetry/OpenTelemetry.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java b/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java index 022933ac0d..3b80f6837d 100644 --- a/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java +++ b/app/src/common/shared/com/igalia/wolvic/telemetry/OpenTelemetry.java @@ -20,16 +20,16 @@ import io.opentelemetry.exporter.logging.LoggingSpanExporter; public class OpenTelemetry implements ITelemetry { - private final Application application; - private OpenTelemetryRum rum; - private OpenTelemetryRumBuilder rumBuilder; - private final String instrumentationScopeName = BuildConfig.APPLICATION_ID; - private final String instrumentationScopeVersion = "1.0.0"; - private final Executor diskIOExecutor; + private final Application mApplication; + private OpenTelemetryRum mRUM; + private OpenTelemetryRumBuilder mRUMBuilder; + private final String INSTRUMENTATION_SCOPE_NAME = BuildConfig.APPLICATION_ID; + private final String INSTRUMENTATION_SCOPE_VERSION = "1.0.0"; + private final Executor mDiskIOExecutor; public OpenTelemetry(Application app) { - application = app; - diskIOExecutor = ((VRBrowserApplication) application).getExecutors().diskIO(); + mApplication = app; + mDiskIOExecutor = ((VRBrowserApplication) mApplication).getExecutors().diskIO(); } private void initializeOpenTelemetryAndroid() { @@ -40,36 +40,36 @@ private void initializeOpenTelemetryAndroid() { .build(); OtelRumConfig config = new OtelRumConfig() .setDiskBufferingConfiguration(diskBufferingConfiguration); - rumBuilder = OpenTelemetryRum.builder(application, config) + mRUMBuilder = OpenTelemetryRum.builder(mApplication, config) .addSpanExporterCustomizer(exporter -> LoggingSpanExporter.create()) .addInstrumentation(new SessionInstrumentation()) .addInstrumentation(new ActivityLifecycleInstrumentation()); try { - rum = rumBuilder.build(); + mRUM = mRUMBuilder.build(); } catch (Exception e) { e.printStackTrace(); } } - private void runOnDiskIO(Runnable runnable) { diskIOExecutor.execute(runnable); } + private void runOnDiskIO(Runnable runnable) { mDiskIOExecutor.execute(runnable); } @Override public void start() { - assert rum == null; + assert mRUM == null; initializeOpenTelemetryAndroid(); } @Override public void stop() { - rum = null; - rumBuilder = null; + mRUM = null; + mRUMBuilder = null; } @Override public void customEvent(String name) { - assert rum != null; + assert mRUM != null; runOnDiskIO(() -> { - rum.getOpenTelemetry().getTracer(instrumentationScopeName, instrumentationScopeVersion) + mRUM.getOpenTelemetry().getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION) .spanBuilder(name) .startSpan() .end(); @@ -78,9 +78,9 @@ public void customEvent(String name) { @Override public void customEvent(String name, Bundle bundle) { - assert rum != null; + assert mRUM != null; runOnDiskIO(() -> { - SpanBuilder spanBuilder = rum.getOpenTelemetry().getTracer(instrumentationScopeName, instrumentationScopeVersion) + SpanBuilder spanBuilder = mRUM.getOpenTelemetry().getTracer(INSTRUMENTATION_SCOPE_NAME, INSTRUMENTATION_SCOPE_VERSION) .spanBuilder(name); for (String key : bundle.keySet()) { spanBuilder.setAttribute(key, bundle.get(key).toString());