From 58667fb6cd2ef521e4aa38f27cafe5c057e5656b Mon Sep 17 00:00:00 2001 From: Sujoy Adhikary Date: Fri, 10 Apr 2026 12:41:15 +0530 Subject: [PATCH 1/4] datadog setup - setup - service name - user id set - config change --- apps/build.gradle | 1 + .../src/main/java/GlobalDependencies.kt | 3 + apps/student/build.gradle | 9 ++- .../student/fragment/DashboardFragment.kt | 2 + .../instructure/student/util/AppManager.kt | 79 ++++++++++++++++++- 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/apps/build.gradle b/apps/build.gradle index e74b6b143..57bbb70a5 100644 --- a/apps/build.gradle +++ b/apps/build.gradle @@ -34,6 +34,7 @@ buildscript { classpath Plugins.FIREBASE_CRASHLYTICS if (project.coverageEnabled) { classpath Plugins.JACOCO_ANDROID } classpath Plugins.HILT + classpath Plugins.DATADOG } } diff --git a/apps/buildSrc/src/main/java/GlobalDependencies.kt b/apps/buildSrc/src/main/java/GlobalDependencies.kt index 25aa35d53..6622d7079 100644 --- a/apps/buildSrc/src/main/java/GlobalDependencies.kt +++ b/apps/buildSrc/src/main/java/GlobalDependencies.kt @@ -41,6 +41,7 @@ object Versions { const val DATASTORE = "1.1.1" const val LOTTIE = "6.5.2" const val ENCRYPTED_SHARED_PREFERENCES = "1.0.0" + const val DATADOG = "1.22.0" } object Libs { @@ -200,4 +201,6 @@ object Plugins { const val GOOGLE_SERVICES = "com.google.gms:google-services:${Versions.GOOGLE_SERVICES}" const val JACOCO_ANDROID = "com.dicedmelon.gradle:jacoco-android:${Versions.JACOCO_ANDROID}" const val HILT = "com.google.dagger:hilt-android-gradle-plugin:${Versions.HILT}" + + const val DATADOG = "com.datadoghq:dd-sdk-android-gradle-plugin:${Versions.DATADOG}" } diff --git a/apps/student/build.gradle b/apps/student/build.gradle index 1d0d6f50e..7c2967fdf 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -46,7 +46,8 @@ android { testInstrumentationRunnerArguments disableAnalytics: 'true' buildConfigField "boolean", "IS_TESTING", "false" buildConfigField "String", "BUGFENDER_KEY", "\"${Keys.BUGFENDER_KEY}\"" - + buildConfigField "String", "DATADOG_CLIENT_TOKEN", "\"${Keys.DATADOG_CLIENT_TOKEN}\"" + buildConfigField "String", "DATADOG_APPLICATION_ID", "\"${Keys.DATADOG_APPLICATION_ID}\"" /* Add private data */ PrivateData.merge(project, "student") @@ -359,6 +360,12 @@ dependencies { implementation "com.amazonaws:aws-android-sdk-lambda:2.71.0" implementation "com.amazonaws:aws-android-sdk-sns:2.71.0" implementation("com.bugfender.sdk:android:3.0.17") + + //DATADOG + implementation("com.datadoghq:dd-sdk-android-rum:3.5.0") + implementation("com.datadoghq:dd-sdk-android-logs:3.5.0") + implementation("com.datadoghq:dd-sdk-android-okhttp:3.5.0") + implementation("com.datadoghq:dd-sdk-android-trace:3.5.0") } // Comment out this line if the reporting logic starts going wonky. diff --git a/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt b/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt index 084492212..301251297 100644 --- a/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt +++ b/apps/student/src/main/java/com/instructure/student/fragment/DashboardFragment.kt @@ -38,6 +38,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.work.WorkInfo.State import androidx.work.WorkManager import androidx.work.WorkQuery +import com.datadog.android.Datadog import com.google.firebase.crashlytics.FirebaseCrashlytics import com.instructure.canvasapi2.managers.CourseNicknameManager import com.instructure.canvasapi2.managers.UserManager @@ -568,6 +569,7 @@ class DashboardFragment : ParentFragment() { private fun initFirebase() { val user = ApiPrefs.user ?: return FirebaseAnalytics.identifyUser(user.id) + Datadog.setUserInfo(user.id.toString(), null, null) } companion object { diff --git a/apps/student/src/main/java/com/instructure/student/util/AppManager.kt b/apps/student/src/main/java/com/instructure/student/util/AppManager.kt index e20cb40f9..18bc818d4 100644 --- a/apps/student/src/main/java/com/instructure/student/util/AppManager.kt +++ b/apps/student/src/main/java/com/instructure/student/util/AppManager.kt @@ -18,6 +18,7 @@ package com.instructure.student.util import android.content.Intent +import android.util.Log import androidx.core.content.ContextCompat import androidx.hilt.work.HiltWorkerFactory import androidx.work.Constraints @@ -27,6 +28,14 @@ import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import androidx.work.WorkerFactory import com.bugfender.sdk.Bugfender +import com.datadog.android.Datadog +import com.datadog.android.core.configuration.Configuration +import com.datadog.android.log.Logger +import com.datadog.android.log.Logs +import com.datadog.android.log.LogsConfiguration +import com.datadog.android.privacy.TrackingConsent +import com.datadog.android.rum.RumConfiguration +import com.datadog.android.trace.DatadogTracing import com.instructure.canvasapi2.utils.MasqueradeHelper import com.instructure.loginapi.login.tasks.LogoutTask import com.instructure.pandautils.analytics.pageview.PageViewUploadWorker @@ -47,9 +56,14 @@ import com.twou.offline.util.BaseOfflineUtils.Companion.isOnline import com.twou.offline.util.OfflineLoggerType import com.twou.offline.util.OfflineLogs import dagger.hilt.android.HiltAndroidApp -import sdk.pendo.io.Pendo import java.util.concurrent.TimeUnit import javax.inject.Inject +import com.datadog.android.trace.GlobalDatadogTracer +import com.datadog.android.trace.Trace +import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.trace.event.SpanEventMapper +import com.datadog.android.trace.model.SpanEvent +import com.datadog.android.rum.Rum @HiltAndroidApp class AppManager : BaseAppManager() { @@ -68,6 +82,7 @@ class AppManager : BaseAppManager() { @Inject lateinit var workManager: WorkManager + lateinit var logger: Logger override fun onCreate() { super.onCreate() @@ -197,8 +212,70 @@ class AppManager : BaseAppManager() { schedulePandataUpload() initPendo() + setupDataDog() } + private fun setupDataDog() { + val configuration = Configuration.Builder( + clientToken = BuildConfig.DATADOG_CLIENT_TOKEN, + env = if (BuildConfig.BUILD_TYPE == "release") "production" else "staging", + service = "degrees-mobile" + ).build() + + Datadog.initialize(this, configuration, trackingConsent = TrackingConsent.GRANTED) + + Trace.equals( + TraceConfiguration.Builder().setEventMapper(object : SpanEventMapper { + override fun map(event: SpanEvent): SpanEvent { + val originalUrl = event.resource + event.resource = redactSensitiveData(originalUrl) + return event + } + }) + .build() + ) + + GlobalDatadogTracer.registerIfAbsent( + DatadogTracing.newTracerBuilder() + .build() + ) + + Datadog.setVerbosity(Log.INFO) + + val logsConfig = LogsConfiguration.Builder().build() + Logs.enable(logsConfig) + + logger = Logger.Builder() + .setNetworkInfoEnabled(true) + .setLogcatLogsEnabled(true) + .setName("GetSmarterAndroid") + .build() + + val rumConfig = RumConfiguration.Builder(BuildConfig.DATADOG_APPLICATION_ID) + .setResourceEventMapper { event -> + val originalUrl = event.resource.url + event.resource.url = redactSensitiveData(originalUrl) + event + } + .setErrorEventMapper { event -> + event.error.message = redactSensitiveData(event.error.message) + event.error.resource?.url = redactSensitiveData(event.error.resource?.url.orEmpty()) + event + } + .trackUserInteractions() + .trackAnonymousUser(true) + .trackLongTasks() + .trackBackgroundEvents(true) + .trackNonFatalAnrs(true) + .build() + Rum.enable(rumConfig) + } + private fun redactSensitiveData(message: String) = message.replace( + Regex( + "(username|password|wstoken|token)=[^&]*", + RegexOption.IGNORE_CASE + ), "$1=****" + ) private fun initPendo() { // val options = Pendo.PendoOptions.Builder().setJetpackComposeBeta(true).build() // Pendo.setup(this, BuildConfig.PENDO_TOKEN, options, null) From 43aaa977f6de05ad5c485f2068c0af52797110c1 Mon Sep 17 00:00:00 2001 From: Sujoy Adhikary Date: Mon, 13 Apr 2026 21:06:00 +0530 Subject: [PATCH 2/4] dg Interceptor --- automation/dataseedingapi/build.gradle | 2 + .../dataseeding/util/CanvasNetworkAdapter.kt | 68 ++++++++++--------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/automation/dataseedingapi/build.gradle b/automation/dataseedingapi/build.gradle index 4828823e1..55dbe5ab6 100644 --- a/automation/dataseedingapi/build.gradle +++ b/automation/dataseedingapi/build.gradle @@ -47,6 +47,8 @@ dependencies { implementation 'com.github.javafaker:javafaker:0.17.2' implementation Libs.APOLLO_RUNTIME + /* Datadog */ + implementation 'com.datadoghq:dd-sdk-android-okhttp:3.5.0' /* Test Dependencies */ testImplementation Libs.JUNIT diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt index e83efde3f..ae105a699 100644 --- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt +++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt @@ -19,6 +19,7 @@ package com.instructure.dataseeding.util import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.network.okHttpClient +import com.datadog.android.okhttp.DatadogInterceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit @@ -48,38 +49,45 @@ object CanvasNetworkAdapter { return loggingInterceptor } + private fun createClientBuilder( + token: String? = null, + retryOnConnectionFailure: Boolean, + includeRestRetry: Boolean, + ): OkHttpClient.Builder { + val builder = OkHttpClient.Builder() + .retryOnConnectionFailure(retryOnConnectionFailure) + .addInterceptor(DatadogInterceptor()) + .addInterceptor(getLoggingInterceptor()) + .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) + + token?.let { builder.addInterceptor(AuthRequestInterceptor(it)) } + if (includeRestRetry) builder.addInterceptor(RestRetryInterceptor) + + return builder + } + private val adminOkHttpClient: OkHttpClient by lazy { - val authInterceptor = AuthRequestInterceptor(adminToken) - - OkHttpClient.Builder() - .retryOnConnectionFailure(true) - .addInterceptor(authInterceptor) - .addInterceptor(getLoggingInterceptor()) - .addInterceptor(RestRetryInterceptor) - .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) - .build() + createClientBuilder( + token = adminToken, + retryOnConnectionFailure = true, + includeRestRetry = true, + ).build() } private fun okHttpClientWithToken(token: String): OkHttpClient { - val authInterceptor = AuthRequestInterceptor(token) - - return OkHttpClient.Builder() - .retryOnConnectionFailure(true) - .addInterceptor(authInterceptor) - .addInterceptor(getLoggingInterceptor()) - .addInterceptor(RestRetryInterceptor) - .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) - .build() + return createClientBuilder( + token = token, + retryOnConnectionFailure = true, + includeRestRetry = true, + ).build() } private fun okHttpClientForApollo(token: String): OkHttpClient { - val authInterceptor = AuthRequestInterceptor(token) - - return OkHttpClient.Builder() - .addInterceptor(authInterceptor) - .addInterceptor(getLoggingInterceptor()) - .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) - .build() + return createClientBuilder( + token = token, + retryOnConnectionFailure = false, + includeRestRetry = false, + ).build() } fun getApolloClient(token: String): ApolloClient { @@ -90,12 +98,10 @@ object CanvasNetworkAdapter { } private val noAuthOkHttpClient: OkHttpClient by lazy { - OkHttpClient.Builder() - .retryOnConnectionFailure(true) - .addInterceptor(getLoggingInterceptor()) - .addInterceptor(RestRetryInterceptor) - .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) - .build() + createClientBuilder( + retryOnConnectionFailure = true, + includeRestRetry = true, + ).build() } val adminRetrofit: Retrofit by lazy { From 5a3c4d87605785b663733663f4dcaaa7bec29e43 Mon Sep 17 00:00:00 2001 From: srajupusapati Date: Wed, 22 Apr 2026 13:33:03 +0530 Subject: [PATCH 3/4] Added Datadog Network calls --- apps/student/build.gradle | 8 ++++---- automation/dataseedingapi/build.gradle | 3 ++- .../instructure/dataseeding/util/CanvasNetworkAdapter.kt | 4 +++- libs/canvas-api-2/build.gradle | 4 ++++ .../java/com/instructure/canvasapi2/CanvasRestAdapter.kt | 8 ++++++++ 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/apps/student/build.gradle b/apps/student/build.gradle index 7c2967fdf..a37bbb7d1 100644 --- a/apps/student/build.gradle +++ b/apps/student/build.gradle @@ -362,10 +362,10 @@ dependencies { implementation("com.bugfender.sdk:android:3.0.17") //DATADOG - implementation("com.datadoghq:dd-sdk-android-rum:3.5.0") - implementation("com.datadoghq:dd-sdk-android-logs:3.5.0") - implementation("com.datadoghq:dd-sdk-android-okhttp:3.5.0") - implementation("com.datadoghq:dd-sdk-android-trace:3.5.0") + implementation("com.datadoghq:dd-sdk-android-rum:3.8.0") + implementation("com.datadoghq:dd-sdk-android-logs:3.8.0") + implementation("com.datadoghq:dd-sdk-android-okhttp:3.8.0") + implementation("com.datadoghq:dd-sdk-android-trace:3.8.0") } // Comment out this line if the reporting logic starts going wonky. diff --git a/automation/dataseedingapi/build.gradle b/automation/dataseedingapi/build.gradle index 55dbe5ab6..25c37b71c 100644 --- a/automation/dataseedingapi/build.gradle +++ b/automation/dataseedingapi/build.gradle @@ -48,7 +48,8 @@ dependencies { implementation Libs.APOLLO_RUNTIME /* Datadog */ - implementation 'com.datadoghq:dd-sdk-android-okhttp:3.5.0' + implementation 'com.datadoghq:dd-sdk-android-okhttp:3.8.0' + implementation("com.datadoghq:dd-sdk-android-trace:3.8.0") /* Test Dependencies */ testImplementation Libs.JUNIT diff --git a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt index ae105a699..4501b54a9 100644 --- a/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt +++ b/automation/dataseedingapi/src/main/kotlin/com/instructure/dataseeding/util/CanvasNetworkAdapter.kt @@ -19,6 +19,7 @@ package com.instructure.dataseeding.util import com.apollographql.apollo.ApolloClient import com.apollographql.apollo.network.okHttpClient +import com.datadog.android.okhttp.DatadogEventListener import com.datadog.android.okhttp.DatadogInterceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -56,7 +57,8 @@ object CanvasNetworkAdapter { ): OkHttpClient.Builder { val builder = OkHttpClient.Builder() .retryOnConnectionFailure(retryOnConnectionFailure) - .addInterceptor(DatadogInterceptor()) + .addInterceptor(DatadogInterceptor.Builder(listOf("*.com")).build()) + .eventListenerFactory(DatadogEventListener.Factory()) .addInterceptor(getLoggingInterceptor()) .readTimeout(TIMEOUT_IN_SECONDS, TimeUnit.SECONDS) diff --git a/libs/canvas-api-2/build.gradle b/libs/canvas-api-2/build.gradle index 8428d48ad..a70abc5bc 100644 --- a/libs/canvas-api-2/build.gradle +++ b/libs/canvas-api-2/build.gradle @@ -112,6 +112,10 @@ dependencies { implementation Libs.OKHTTP_URL_CONNECTION implementation Libs.OKIO + /* Datadog */ + implementation 'com.datadoghq:dd-sdk-android-okhttp:3.8.0' + implementation("com.datadoghq:dd-sdk-android-trace:3.8.0") + /* GraphQL */ /* NOTE: Newer versions of OkHttp affect the encoding of square brackets in @Query names. We're excluding the * okhttp module here to avoid updating to a newer version until we're ready to handle the change. */ diff --git a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/CanvasRestAdapter.kt b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/CanvasRestAdapter.kt index 0f9421a23..f03018986 100644 --- a/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/CanvasRestAdapter.kt +++ b/libs/canvas-api-2/src/main/java/com/instructure/canvasapi2/CanvasRestAdapter.kt @@ -17,6 +17,8 @@ package com.instructure.canvasapi2 import android.net.http.HttpResponseCache +import com.datadog.android.okhttp.DatadogEventListener +import com.datadog.android.okhttp.DatadogInterceptor import com.google.gson.GsonBuilder import com.instructure.canvasapi2.builders.RestParams import com.instructure.canvasapi2.calladapter.DataResultCallAdapterFactory @@ -65,6 +67,8 @@ protected constructor(var statusCallback: StatusCallback<*>?, private val authUs OkHttpClient.Builder() .addNetworkInterceptor(PactRequestInterceptor(authUser)) .addNetworkInterceptor(ResponseInterceptor()) + .addInterceptor(DatadogInterceptor.Builder(listOf("*.com")).build()) + .eventListenerFactory(DatadogEventListener.Factory()) .readTimeout(TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) .authenticator(canvasAuthenticator) .dispatcher(mDispatcher) @@ -169,6 +173,8 @@ protected constructor(var statusCallback: StatusCallback<*>?, private val authUs OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .addInterceptor(RollCallInterceptor()) + .addInterceptor(DatadogInterceptor.Builder(listOf("*.com")).build()) + .eventListenerFactory(DatadogEventListener.Factory()) .authenticator(canvasAuthenticator) .readTimeout(TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) .dispatcher(mDispatcher) @@ -310,6 +316,8 @@ protected constructor(var statusCallback: StatusCallback<*>?, private val authUs .addInterceptor(loggingInterceptor) .addInterceptor(RequestInterceptor()) .addNetworkInterceptor(ResponseInterceptor()) + .addInterceptor(DatadogInterceptor.Builder(listOf("*.com")).build()) + .eventListenerFactory(DatadogEventListener.Factory()) .readTimeout(TIMEOUT_IN_SECONDS.toLong(), TimeUnit.SECONDS) .dispatcher(mDispatcher) .authenticator(canvasAuthenticator) From ef9b1665ac4ef887c3d69da070ea73b7608dcecc Mon Sep 17 00:00:00 2001 From: srajupusapati Date: Wed, 22 Apr 2026 13:48:52 +0530 Subject: [PATCH 4/4] Update AppManager.kt --- .../src/main/java/com/instructure/student/util/AppManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/student/src/main/java/com/instructure/student/util/AppManager.kt b/apps/student/src/main/java/com/instructure/student/util/AppManager.kt index 18bc818d4..1c81fc44a 100644 --- a/apps/student/src/main/java/com/instructure/student/util/AppManager.kt +++ b/apps/student/src/main/java/com/instructure/student/util/AppManager.kt @@ -248,7 +248,7 @@ class AppManager : BaseAppManager() { logger = Logger.Builder() .setNetworkInfoEnabled(true) .setLogcatLogsEnabled(true) - .setName("GetSmarterAndroid") + .setName("degrees-android") .build() val rumConfig = RumConfiguration.Builder(BuildConfig.DATADOG_APPLICATION_ID)