Mobile App monitoring FAQ

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 forcefull shutdowns occure. 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), in order to prevent any impact on your app's network performance.

Before flushing, beacons joined in batches of 100. Each batch is then transmitted in a single HTTP request (gzipped 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 will wait 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 will be discarded.

If the transmission of beacons receives an error response from the server, the agent will automatically retry the transmission up to 3 times using a 10s exponential backoff mechanism. If this automatic retry fails, beacons will still not be droped, they will 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 if 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?

However, yes consider the limit of 100 beacons that are described here. If 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)?

We recommend 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 an 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 I enable Instana monitoring just in my Android app debug build during the testing phase?

Currently, you can doing this by using the Gradle itself 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

Note: 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 you 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/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.

We currently support 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.

Refer to the Android API to learn more about this.

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 an URLProtocol as delegate. But you can also explicitly ignore any custom URLSession from being monitored through Instana.ignore(yourURLSession). If you are using an URLSession inside your own URLProtocol without a delegate, you should ignore this session in the Instana iOS agent. Otherwise, the request and response is doubled 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 sometimes 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, we don't add a second method to the class. Therefore, we set our new implementation directly (via 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. There is no need to track or set anything client-side.

Which HTTP headers are being used?

To achieve backend correlation, the agent makes use of 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?

iOS: 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 adding 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, 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-know issue, which might occur when your app's resolved OkHttp and OkHttp-logging-interceptor versions differ. This situation is described in more length in: https://github.com/square/okhttp/issues/2839

Instana Android Agent uses OkHttp v4, which might be newer than the OkHttp version your app was using.

To solve this, upgrade your app's OkHttp and OkHttp-logging-interceptor to the available versions.

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

Instana Android Agent makes use of Java's UUIDs.

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

As a work-around, disable penaltyDeath or detectDiskReads for API 18 and lower.

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

The whole error reads: 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, it read as follows:

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, it reads:

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, this means every dependency in a project needs to exclusively use WorkManager 2.1.0+ or previous versions.

Instana Android Agent uses WorkManager 2.4.0+.

If you get this error, the most probably 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, we have found that 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 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 pluging 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 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 the 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

Refer to the documentation specific to each platform for more details.

Sensitive data

Are you collecting data, which can uniquely identify users?

By default, the Instana Agent, does not include data, which can unique identify 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 via the configuration tab in a mobile app's dashboard within the Instana user interface.