Mobile App monitoring FAQ

Mobile App monitoring FAQ covers setup, troubleshooting, and best practices for effective monitoring of mobile applications.

Data collection and reporting

Which app data is collected?

Along with the Instana monitoring, the mobile agent automatically collects the following app-specific information:

  • Bundle or App identifier
  • App and build version
  • Current app language
  • iOS/Android version, device model, hardware, device, and manufacturer
  • Whether a device is jailbroken or rooted
  • Screen size and resolution
  • Carrier name and connection type (2G, 3G, 4G...)

How is beacon reporting handled?

To avoid blocking the main thread (UI), reporting and queue handling is performed in a background thread.

The agent collects all incoming beacons in a persistent queue in the device, so beacons are not lost if unexpected eventualities like crashes or forceful shutdowns occur. Each beacon requires around 600 bytes of storage space.

Generation of beacons is rate-limited to 20 beacons in each 10-second slot, and 500 beacons in each 5-minute slot.

Beacons are flushed one second after the beacon generation stops (ten seconds if the battery is low) to prevent any impact on your app's network performance.

Before you flush, the beacons joined in batches of 100. Each batch is then transmitted in a single HTTP request (compressed if the device supports it, the same HTTP connection if the device supports it).

If the device is offline when the agent tries to send them, the agent waits until the device comes back online to send the beacons.

The queue has a default max limit of 1000 beacons. If more beacons are generated while the queue is full, new beacons are discarded.

If the transmission of beacons receives an error response from the server, the agent automatically retries the transmission up to 3 times by using a 10s exponential backoff mechanism. If this automatic retry fails, the beacons will still not be dropped, they stay in the queue to be retried together with the rest of the queue when new beacons are generated.

What happens for users with bad internet or network connections?

Data transmission requests from your mobile app users to our servers might not be working. In these cases, we attempt to deliver the beacons as part of the next monitoring data transmission, for example, on the following mobile app start.

What happens when reporting fails?

Due to network problems, beacon reporting can fail. The beacons are retained in the queue for the next transmission attempt.

Is data collected while offline?

Yes, consider the limit of 100 beacons that are described here. When the queue is full and cannot be flushed (due to network issues), all upcoming beacons are discarded.

Is it possible to proxy the HTTP endpoints (for SaaS)?

Instana recommends not to attempt the proxying of the HTTP endpoints. Instana does not provide any support for any proxy setups nor for any issues that can arise due to the usage of a proxy. If you still want (or have) to do this, you can find these pointers helpful:

  • Set proper Host HTTP headers.
  • Respect the difference between the eum.instana.io and eum-{region}.instana.io servers.
  • Make sure that our servers are aware of the users IPs. Send X-FORWARDED-FOR header to servers with the user's IP. Alternatively send a X-REALER-IP HTTP header (yes, deliberately not X-REAL-IP) to the Instana servers, which contains the user's IP.
  • Pass through all the HTTP headers that the Instana servers include in the response body.
  • Don't do any caching in the proxy.

Can Instana monitoring be enabled in Android app debug build during the testing phase?

Currently, you can use Gradle to get conditional plug-in usage:

See the following examples in the instana-example directory in the Instana repository:

instana-example/app/build.gradle

instana-example/app/src/main/java/com/instana/mobileeum/DemoApp.kt

Notes:
  • Instana provides two libraries for Android monitoring, the `plugin` library and the `runtime` library. The following steps are based on the build types as follows:
    • Instana `plugin` library is not loaded at all.
    • Instana `runtime` library is still packaged into your app bundle, however, it is left `uninitialized`.

If you don't want any of the Instana codes to be executed in your app, follow the steps:

  1. Get the current BuildType in Gradle.

    In your app/build.gradle, use the following code snippet to query the current build type, and define enableInstana based on the build type.

    def buildType = gradle.startParameter.taskRequests.stream()
        .flatMap { it.args.stream() }
        .map { it.contains("Release") ? "Release" : it.contains("Debug") ? "Debug" : null}
        .filter(Objects::nonNull)
        .findFirst()
    
    buildType.ifPresent { println "buildType is ${it}" }
    def enableInstana = buildType.orElse("").matches("Debug")
    
  2. Apply Instana plugin conditionally.

    if (enableInstana) {
        println "instana> apply instana plugin"
        apply plugin: 'com.instana.android-agent-plugin'
    }
    
  3. Add the Instana Key conditionally in Gradle.

    Then, you have an empty string in the INSTANA_REPORTING_URL field if Instana is disabled.

    android {
        defaultConfig {
            def instanaProperties = new Properties()
            instanaProperties.load(new FileInputStream("$project.projectDir/instana.properties"))
            buildConfigField 'String', 'INSTANA_KEY', enableInstana ? "${instanaProperties["instana.key"]}" : '""'
            buildConfigField 'String', 'INSTANA_REPORTING_URL', enableInstana ? "${instanaProperties["instana.reportingURL"]}" : '""'
        }
    }
    
  4. Initialize the Instana Runtime conditionally.

    Now, the Instana runtime will not be initialized with unwanted build type.

    if (BuildConfig.INSTANA_REPORTING_URL.isNotBlank()) {
        Instana.setup(this, InstanaConfig(
                    reportingURL = BuildConfig.INSTANA_REPORTING_URL,
                    key = BuildConfig.INSTANA_KEY
                ))
    }
    

Sessions

The information that is captured by mobile Instana agents is organized in sessions.

Each session maps to an instance of the app; starts when the app is instantiated and ends when the app is stopped.

Session lifecycle

  1. A session automatically starts every time that the app is started, when the agent is set up
  2. A sessionID is automatically generated for the session
  3. The sessionID remains unchanged while the app is alive (either in the foreground of the background)
  4. All beacons that are sent by the Instana Android agent or iOS agent contains the same sessionID
  5. The sessionID is discarded when the app is stopped; when the process itself is stopped
  6. Next time the app is started, a new sessionID will be automatically generated

HTTP Monitoring

What HTTP information is monitored?

  • The duration of the request
  • HTTP method (POST, GET....)
  • Full URL and path
  • Status code (for example 200)
  • Response sizes (header, body, decoded body)
  • Any underlying error
  • Backend tracing ID for backend correlation

What is the difference between auto and manual HTTP monitoring?

iOS

The iOS agent offers automatic and manual monitoring of HTTP sessions.

Automatic

To monitor HTTP requests and responses going through the URLSession, the automatic HTTP monitoring uses the Foundation's URLProtocol. All captured requests and responses are reported to Instana. While the default URLSession can be intercepted implicitly by URLProtocol, all custom sessions require an explicit URLProtocol registration for monitoring. Because many applications have a custom URLSession, the registration process for those sessions is not the ideal solution for the Instana iOS agent. To simplify the registration process, all sessions are registered implicitly by swizzling the initializer of all URLSessions. With the swizzling approach, no registration of the URLProtocol is required and it simplifies automatic HTTP monitoring. Automatic monitoring of the WKWebView is not available since it's not reliable. The app and the WebView's WebKit run in different processes. There is an IPC between the app and WebKit process but some data (for example POST body) is not transmitted to the app process. This would lead to an unexpected behavior.

Manual

By capturing the HTTP request and response manually, the client can opt out from automatic monitoring and swizzling. No URLProtocol is installed to the URLSessions. Manual monitoring means that when you are capturing HTTP requests and responses, it requires more effort to use the monitoring by the client.

Android

The Android agent offers automatic and manual monitoring of HTTP sessions.

Automatic

The Android plug-in weaves some additional tracking code in your app at compile time.

Instana currently supports automatic tracking of these network clients:

  • OkHttp3
  • HttpURLConnection
  • Retrofit

Register your interest for support of more network clients in the Android agent's issue tracker.

Manual

By capturing the HTTP request and response manually, the client can opt out from automatic monitoring and take complete control over which network requests are tracked and which are not.

For more information, see Android API.

React Native

The React Native agent currently offers automatic of HTTP sessions.

Automatic

Refer to the documentation specific to each platform for more details about automatic monitoring of HTTP sessions.

iOS: Can I use my own URLProtocol with automatic monitoring enabled?

Yes, you can create your own URLProtocol to proxy your requests and responses through a custom URLSession. Instana HTTP monitoring automatically ignores any URLSession that has URLProtocol as delegate. But you can also explicitly ignore any custom URLSession from being monitored through Instana.ignore(yourURLSession). If you are using URLSession inside your own URLProtocol without a delegate, you should ignore this session in the Instana iOS agent. Otherwise, the request and response is double monitored.

iOS: Isn't swizzling dangerous?

Not always. Swizzling in the runtime is mostly done by adding a method:

func swizzled_touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
  // looks weird, but it calls the original implementation
   swizzled_touchesBegan(touches, with: event)
   print("do some custom magic here")
 }

And then the new and old methods are exchanged through:

if let originalMethod = class_getInstanceMethod(UIResponder.self, #selector(touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?))),
   let swizzledMethod = class_getInstanceMethod(UIResponder.self, #selector(swizzled_touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?))) {
        method_exchangeImplementations(originalMethod, swizzledMethod)
}

However, it might lead to unexpected behavior when the runtime makes a forward invocation like in UIResponder. Sometimes the runtime performs message forwarding. This swizzling technique might cause a crash if a message (for example, the new swizzled_touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)) is forwarded to another receiver on which the new method does not exist. In the Instana iOS agent, you cannot add a second method to the class. Therefore, you set a new implementation directly (through a block) to the original selector and override the original implementation. The original implementation, temporarily stored, is then called inside the new function; so swizzling leaves no traces.

iOS: Is monitoring for background sessions available?

No, because custom URLProtocol subclasses are not available to background sessions. (See URLSession doc.)

How does backend correlation work with HTTP requests?

All HTTP requests include automatic backend correlation, to backends that are monitored by Instana's host agent. You might not need to track or set anything client-side.

Which HTTP headers are being used?

To achieve backend correlation, the agent uses the following HTTP headers:

  • Response Headers:
    • Server-Timing

Requirements

iOS

Which iOS versions are supported?

iOS: Instana iOS agent is compatible from iOS 11.

Which Swift versions are supported?

Swift 5 (since Xcode 10.2) or higher.

How do I install the iOS agent?

Use the Swift Package Manager inside Xcode. Alternatively, you can use CocoaPods. To set up the Instana iOS agent, call one method in application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:.

Android

Which Android versions are supported?

Minimum supported API level: Android API 16 (Android 4.1 "Jelly Bean").

AndroidX: AndroidX is supported and required.

Which Languages are supported?

Languages: Both Java and Kotlin are supported.

Java compatibility target: Java 1.8 or higher.

What does the Duplicate class com.google.common.util.concurrent.ListenableFuture compilation error mean?

If your app has a dependency on guava, you might get the following compilation error after you add the Instana Android agent.

Duplicate class com.google.common.util.concurrent.ListenableFuture found in modules guava-25.0-android.jar (com.google.guava:guava:25.0-android) and listenablefuture-1.0.jar (com.google.guava:listenablefuture:1.0)

To solve this problem, add the following dependency to your app:

implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'

What does the NoSuchMethodError: No virtual method log(Ljava/lang/String;)V in class Lokhttp3/internal/Platform; runtime error mean?

This links to a well-known issue, which might occur when your applications resolved OkHttp and OkHttp-logging-interceptor versions differ. This situation is described in more lengths in: https://github.com/square/okhttp/issues/2839.

Instana Android agent uses OkHttp v4, which might be newer than the OkHttp.

To solve this problem, upgrade your applications OkHttp and OkHttp-logging-interceptor to the available versions.

What does the android.os.StrictMode$StrictModeDiskReadViolation StrictMmode error mean?

Instana Android agent uses Java's UUIDs.

A well-known false positive is raised by StrictMode in Android API 18 and earlier: https://issuetracker.google.com/issues/36969031.

Workaround: Disable penaltyDeath or detectDiskReads for API 18 and earlier.

What does the java.lang.NoSuchMethodError: No virtual method setInitialDelay compilation error mean?

Error: java.lang.NoSuchMethodError: No virtual method setInitialDelay(JLjava/util/concurrent/TimeUnit;)Landroidx/work/OneTimeWorkRequest$Builder; in class Landroidx/work/OneTimeWorkRequest$Builder; or its super classes (declaration of 'androidx.work.OneTimeWorkRequest$Builder'

WorkManager 2.1.0 changed the method signature for setInitialDelay. Before WorkManager 2.1.0:

abstract class WorkRequest {
    abstract static class Builder<B extends Builder, W extends WorkRequest> {
    }
}

class OneTimeWorkRequest extends WorkRequest {
   static class Builder extends WorkRequest.Builder<Builder, OneTimeWorkRequest> {
       public @NonNull Builder setInitialDelay(long duration, @NonNull TimeUnit timeUnit) {
          ....
       }
  }
}

After WorkManager 2.1.0:

abstract class WorkRequest {
    abstract static class Builder<B extends Builder, W extends WorkRequest> {
         public @NonNull B setInitialDelay(long duration, @NonNull TimeUnit timeUnit) {
              ...
         }
    }
}

class OneTimeWorkRequest extends WorkRequest {
   static class Builder extends WorkRequest.Builder<Builder, OneTimeWorkRequest> {
  }
}

In effect, every dependency in a project needs to exclusively use WorkManager 2.1.0+ or earlier versions.

Instana Android agent uses WorkManager 2.4.0+.

If you get this error, the most probable issue is that another dependency is using an older version of WorkManager. Make sure to update it.

What does the Required: PROJECT. Found: EXTERNAL_LIBRARIES, PROJECT, SUB_PROJECTS compilation error mean?

This error is known to open when a Gradle plug-in, which requires an Android Gradle plug-in older than 3.6.1 is compiled by using Android Gradle plug-in 3.6.1 or higher.

For instance, by using Realm Database plug-in 7.0.1 with Android Gradle plug-in 4.0.1 and Instana Android agent 3.4.0 can give way to this compilation error. The resulting error log contains a reference to Realm's transformer task's failure first, and then maybe to some other plug-in (not relevant):

[...]
Task :app:transformClassesWithRealmTransformerForDevevelopDebug FAILED
[...]

To solve this issue, try to upgrade the offending plug-in.

Alternatively, either:

  1. Make sure that the Instana Android agent plug-in is started after the offending plug-in. For example,

    apply plugin: 'com.android.application'
    apply plugin: 'realm-android'
    apply plugin: 'com.instana.android-agent-plugin'
    
    
  2. Downgrade your app's Android Gradle plug-in to the one that the offending plugging requires

What does the Problem processing attributes... compilation error in ajc-transform mean?

As of today, it seems like one of the bytecode transformations that are applied by the Firebase Performance plug-in has the adverse effect of preventing the Instana Android agent from doing its job.

To prevent the issue, make sure that Instana's Android agent is run before Firebase Performance plug-in.

You can achieve this by changing the order in which you add each plug-in into your project:

apply plugin: 'com.instana.android-agent-plugin' // Instana agent will be applied before Firebase Performance
apply plugin: 'com.google.firebase.firebase-perf'

What does the Unable to find method... compilation error mean?

Each major Instana Android agent version is compatible with a specific Android Gradle plug-in branch.

This error is most likely to show up when the Android Gradle plug-in being used isn't supported by the Instana Android agent.

Refer to the Android Gradle plug-in and Gradle version section of the documentation to check the compatibility matrix and learn where to find your app's current Android Gradle plug-in version.

What does the Failed resolution of: Landroidx/work/impl/utils/futures/AbstractFuture runtime error mean?

The whole error reads: Fatal Exception: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/work/impl/utils/futures/AbstractFuture;

It might happen if your app dependencies at some point required a dependency on 9999.0-empty-to-avoid-conflict-with-guava but it no longer requires it.

If your app's contains the following dependency, try removing it:

implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'

What does the Execution failed for task ':app:transformClassesWithDexBuilderFor...' compilation error mean?

The whole error reads (for the DevDebug flavor, for instance):

FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:transformClassesWithDexBuilderForDevDebug'.
> There were multiple failures while executing work items
   > A failure occurred while executing com.android.build.gradle.internal.transforms.DexArchiveBuilderTransform$DexConversionWorkAction
      > Failed to process /Users/developer/Projects/mobile-app/android/app/build/intermediates/javac/devDebug/classes
[...]

This error highlights a mismatch between the Instana agent version and the Android Gradle plug-in versions. Refer to the platform-specific installation instructions to find out about the supported combinations.

React Native

The Instana React Native agent invokes iOS agent on the iOS platform and invokes Android agent on the Android platform. iOS agent or Android agent might be upgraded with React Native agent upgrade. Refer to the documentation specific to each platform for more details.

How to clean up the cached iOS agent and Android agent?

When the React Native agent is upgraded to a new version from your mobile app, run the following command from terminal to thoroughly clean-up the cached old version of iOS agent and Android agent:

cd android && ./gradlew clean
cd ..
cd ios && pod cache clean --all && rm -rf build
cd ..
rm -rf node_modules
yarn cache clean --force
yarn install
cd ios && pod install

Sensitive data

Are you collecting data, which can uniquely identify users?

By default, the Instana agent does not include data, which can identify unique users. Additionally, the agent also does not apply techniques such as device fingerprinting.

User-specific data can be made available to Instana through the iOS user API or the Android user API.

What are you doing with the user data transmitted to Instana?

The Instana agent can be configured by customers to transmit user-identifying information to Instana. This information is only used to provide the features visible to you within Instana. Instana does not interpret this data in any other way, nor is it correlated across customers.

Is it possible to delete user data after it is transmitted to Instana?

Infrequent deletion requests, for example to comply with GDPR, are supported. If you expect frequent or periodic deletion requests, instead transmit anonymized data to Instana (for example hashed user IDs).

Are you anonymizing IPs?

Yes, IPs are anonymized. By default, the last octet of IPv4 addresses and the last 80 bits of IPv6 addresses are set to zeros. Stricter anonymization rules can be configured through the configuration tab in a mobile app's dashboard within the Instana user interface.