Android SDK Error Analysis

Last updated on

Introduction

Prerequisites

Screen tracking implemented

Tracking will start at the 1st screenview event, it is required to have screen tracking implemented. Make sure to follow the Android Track screens sections.

If you are in the process of implementing the SDK for the 1st time (or choose to take this update as an opportunity to review your Privacy related implementation), make sure to follow the Android Privacy sections.

Get Started

Add Contentsquare Error Analysis to your app

A new Gradle dependency needs to be added to your Gradle build file.

[tab] Groovy

implementation "com.contentsquare.android:error-analysis:4.18.2"

[tab] Kotlin

implementation("com.contentsquare.android:error-analysis:4.18.2")

Start the SDK

You do not need to do anything to start the SDK, it will start itself with Contentsquare SDK.

Validate SDK integration

When the SDK starts, you should see a log like this one:

// success case
Contentsquare Error Analysis [SDKVersionNumber] starting in app : [your.app.packageName]

// fail case
// << No public fail log

Sample app

For best implementation practices of our library, explore the Contentsquare for Android sample app.

API Errors

Automatic network inspection

By using code instrumentation, network calls will be instrumented to collect network data.
This way, three largely used network stacks are supported, and all the libraries that use them:

Adding the Gradle plugin

With the use of a Gradle plugin, an instrumentation of your code will be performed to collect the data about network errors.

[tab] Groovy

Using the plugins DSL:

app/build.gradle
plugins {
   id "com.contentsquare.error.analysis.network"  version "1.1.0"  
}

Using legacy plugin application:

build.gradle
buildscript {
   repositories {
      maven {
         url "https://plugins.gradle.org/m2"
      }
   }
   dependencies {
      classpath "com.contentsquare.gradle:error-analysis-network:1.1.0"
   }
}
app/build.gradle
apply plugin: "com.contentsquare.error.analysis.network"
[tab] Kotlin

Using the plugins DSL:

app/build.gradle.kts
plugins {
   id("com.contentsquare.error.analysis.network")  version "1.1.0"  
}

Using legacy plugin application:

build.gradle.kts
buildscript {
   repositories {
      maven {
         url = uri("https://plugins.gradle.org/m2")
      }
   }
   dependencies {
      classpath("com.contentsquare.gradle:error-analysis-network:1.1.0")
   }
}
app/build.gradle.kts
apply(plugin = "com.contentsquare.error.analysis.network")

Task added

The plugin adds new tasks for network code instrumentation: transformVariantClassesWithAsm

This task can be time consuming, and should be disabled for development build.

Configure Gradle plugin

By default the plugin is enabled for each variant, there are three ways to disable the Gradle plugin:

Build type
[tab] Groovy
build.gradle
android {
   buildTypes {
      debug {
         errorAnalysisNetwork {
               setInstrumentationEnabled(false)
         }
      }
   }
}
[tab] Kotlin
build.gradle.kts
android {
   buildTypes {
      debug {
         configure<com.contentsquare.gradle.error.analysis.network.ErrorAnalysisNetworkExtension> {
               setInstrumentationEnabled(false)
         }
      }
   }
}
By flavor
[tab] Groovy
build.gradle
android {
   flavorDimensions "example"
   productFlavors {
      enable {
         dimension "example"
      }
      disable {
         dimension "example"
         errorAnalysisNetwork {
               setInstrumentationEnabled(false)
         }
      }
   }
}
[tab] Kotlin
build.gradle.kts
android {
   flavorDimensions += "example"
   productFlavors {
      create("enable") {
         dimension = "example"
      }
      create("disable") {
         dimension = "example"
         configure<com.contentsquare.gradle.error.analysis.network.ErrorAnalysisNetworkExtension> {
               setInstrumentationEnabled(false)
         }
      }
   }
}
With a Gradle properties
gradle.properties
errorAnalysisNetworkInstrumentationEnabled=false

Command line:

./gradlew :app:assembleDebug -PerrorAnalysisNetworkInstrumentationEnabled=false

Manual inspection

OkHttp interceptor

If your project is using OkHttp client, an interceptor (ErrorAnalysisInterceptor) can be added to collect all the network errors:

[tab] Java
import com.contentsquare.android.error.analysis.network.ErrorAnalysisInterceptor;
 
new OkHttpClient().newBuilder()
   .addInterceptor(new ErrorAnalysisInterceptor())
   .build();
[tab] Kotlin
import com.contentsquare.android.error.analysis.network.ErrorAnalysisInterceptor
 
OkHttpClient().newBuilder()
   .addInterceptor(ErrorAnalysisInterceptor())
   .build()

Custom implementation

If no other solution works, a custom implementation can be created via the newNetworkMetric method.

Multiple data will need to be gathered: URL and method at the creation of the metric, and status code after the request is done. Also calling start (before the request) and stop (after all the rest) methods for time metrics to be collect and to send the event.

[tab] Java
import com.contentsquare.android.error.analysis.ErrorAnalysis;
import com.contentsquare.android.error.analysis.NetworkMetric;
 
 
NetworkMetric metric = ErrorAnalysis.getInstance().newNetworkMetric(url, ErrorAnalysis.HttpMethod.GET);
metric.start();
 
// do your request
 
metric.setStatusCode(code);
metric.stop();
[tab] Kotlin
import com.contentsquare.android.error.analysis.ErrorAnalysis
import com.contentsquare.android.error.analysis.trace
 
ErrorAnalysis.getInstance().newNetworkMetric(url, ErrorAnalysis.HttpMethod.GET).trace {
   
   // do your request
 
   setStatusCode(code)
}

Note: start and stop are automatically called by trace

Removing Personal Data in request URL path

By default, the API Errors feature collects the URL path of the failed API requests. To prevent the collection of Personal Data in the URL path, you can rewrite the request URL path with the ErrorAnalysis.setUrlMaskingPatterns SDK API.

Simply replace a step of the path - meaning between two slashes (/) - containing Personal Data with a variable:

  • becomes CS_ANONYMIZED_USER_ID
  • becomes CS_ANONYMIZED_ADDRESS

Example

[tab] Java
ErrorAnalysis.setUrlMaskingPatterns(
   List.of("https://www.api.com/users/:user_id/address/:address")
);
[tab] Kotlin
ErrorAnalysis.setUrlMaskingPatterns(
    listOf("https://www.api.com/users/:user_id/address/:address")
)
Result
URL before anonymizationURL after anonymization
https://www.contentsquare.com/users/123/address/castle+blackhttps://www.contentsquare.com/users/CS_ANONYMIZED_USER_ID/address/CS_ANONYMIZED_ADDRESS

Debugging and Logging

If in-app features are enabled, a info log should appear with the details of the event (see Android Debugging and Logging section).:

CSLIB: API Error - 401 GET https://api.client.com

How API Errors works

Initialization

The way our SDK works is by auto-starting with the application launch and collects the API errors through the instrumented code added by the Gradle plugin.

Configuration

Once started, our SDK fetches its config from our servers. It will start collecting data from network events if the API Errors setting is enabled in the config (this is handled by the Contentsquare team).

Tracking

The SDK monitors the only API Error with response code above 400, and generates analytics data. These events are then locally stored, and eventually sent to our servers in batches.

Sending data

For each network error, a new event will be sent in analytics and Session Replay data. Check the following sections to learn more about how data is processed and sent:

API Troubleshooting Details

API Errors troubleshooting details enables you to collect more information about API errors so you can troubleshoot errors faster.

With this feature you will be able to see three types of additional API error details in the Event Stream of Session Replay.

  • The HTTP headers of the request and the response.
  • The body (the data sent by the request or received in the response).
  • The query parameters of the request endpoint (of the URL of the information you request for).

See API Troubleshooting Details for more details.

Collected data points

Only network calls in error (response code above 400) will be collected.
Here the exhaustive list of data collected:

  • Response code
  • URL (without query strings)
  • HTTP method
  • Timestamp of the request
  • Timestamp of the response

Known limitations and recommendations

Conflict with Firebase Performance using code instrumentation

Code instrumentation does not work well with Firebase Performance plugin.
Network events will not be logged for OkHttp when using Contentsquare Error Analysis network and Firebase performance plugin (only one of the two will log events).
If you want to use both libraries, prefer using interceptors than code instrumentation.

Crash Reporter

Upload mapping file

In order to produce readable crash reports from builds that have been obfuscated, Contentsquare requires you to upload the mapping file produced by your build to our servers. To simplify this process, we've provided a Gradle plugin that you can integrate into your build.

Adding the Gradle plugin

[tab] Groovy

Using the plugins DSL:

app/build.gradle
plugins {
   id "com.contentsquare.error.analysis.crash"  version "1.1.0"
}

Using legacy plugin application:

build.gradle
buildscript {
   repositories {
      maven {
         url "https://plugins.gradle.org/m2"
      }
   }
   dependencies {
      classpath "com.contentsquare.gradle:error-analysis-crash:1.1.0"
   }
}
app/build.gradle
apply plugin: "com.contentsquare.error.analysis.crash"
[tab] Kotlin

Using the plugins DSL:

app/build.gradle.kts
plugins {
   id("com.contentsquare.error.analysis.crash")  version "1.1.0"
}

Using legacy plugin application:

build.gradle.kts
buildscript {
   repositories {
      maven {
         url = uri("https://plugins.gradle.org/m2")
      }
   }
   dependencies {
      classpath("com.contentsquare.gradle:error-analysis-crash:1.1.0")
   }
}
app/build.gradle.kts
apply(plugin = "com.contentsquare.error.analysis.crash")

Configuring the Gradle Plugin

To use the plugin, you must provide it with the relevant credentials to be able to upload the mapping file. You can do this by setting the uploadCredentials property of the errorAnalysisCrash extension in your build.gradle or build.gradle.kts file, as shown below. The errorAnalysisCrash extension is available on all build types and product flavours.

[tab] Groovy
errorAnalysisCrash {
    uploadCredentials {
        projectId = <your project Id>
        clientId = "<your client Id>"
        clientSecret = "<your client secret>"
    }
}
[tab] Kotlin
configure<com.contentsquare.gradle.error.analysis.crash.extensions.ErrorAnalysisCrashExtension> {
    uploadCredentials {
        projectId = <your project Id>
        clientId = "<your client Id>"
        clientSecret = "<your client secret>"
    }
}

Tasks Added

After applying the Gradle plugin, an upload task will be generated for each build variant that has minification enabled. The task will have a name of the form uploadContentsquareMappingFile<VariantName>

Perform Upload

The upload task is independent from any other tasks in build graph. Once your build is complete, and the mapping file has been generated, you can upload it follows:

./gradlew uploadContentsquareMappingFile<VariantName>

Replace <VariantName> with the name of the build variant you want to upload the mapping file for.

ProGuard Configuration

To ensure that we can provide line numbers and file names in the crash reports, add the following configuration to your ProGuard file:

-keepattributes SourceFile,LineNumberTable        # Keep file names and line numbers

Debugging and Logging

To test that the Crash Reporter is working properly, you can force a crash in your application. The next time you start your application, the crash will be sent to our servers. If you have logging enabled, you will see the upload reflected in the log:

I/CSLIB: Crash event detected and sent for userID: <user-id>, session: <session-number> on screen: <screen-number> crashID: <crash-id>

How Crash Reporter works

Initialization

Crash Reporter is started automatically and begins to report basic information for crashes in your application when it is launched.

Configuration

Once started, our SDK fetches its configuration from our servers. It will start reporting crashes if the Crash Reporter setting is enabled (this is handled by the Contentsquare team).

Reporting

When a crash occurs, a report is created and stored locally in the device. Once the app is launched again, the report is sent to our servers and then removed from the local storage.

Sending data

For each crash, a new event will be sent to Analytics and Session Replay data when the app is launched again after the crash occurred and the network conditions allow for the server to be reached.

Collected data points

The Crash Reporter will capture any crashes caused by an unhandled exception in applications written in Java or Kotlin. It will collect information about the application, system, process, threads, stack traces and some other event metadata you can find in collected data points

Known limitations and recommendations

Native crashes from applications written using C or C++ are currently not supported.

Compatibility with other crash reporters

It is possible that the Crash Reporter will work with other SDKs, but we cannot guarantee it. Android has a global error handling class and therefore we rely on other SDKs forwarding the error on to us in the event of a crash, should they have been registered first. If the Contentsquare Crash Reporter is registered first, it will forward on errors to any other registered handlers.

Based on our tests, we can confirm it is compatible with Firebase Crashlytics.

Impact on Performance

We always strive to be non-intrusive, and transparent to the developers of the client app. We apply this rule on the performance as well. These are the technical specifics we can share on performance, if you have any questions feel free to reach out to us.

The following performances results were obtained under the following conditions:

ConditionValue
Device modelPixel 5
Android version13
PropertyValue
SDK size44 KB
Nbr. of methods522
Nbr. of fields65

We used Android Microbenchmark to measure the time added by our implementation.

The instrumented code added is running in less than 1ms per request: our benchmarks show less 400┬Ás.

About CPU and memory, the overhead of adding the collection of network error is negligible.