Skip to content

Integrations

Explore multiple ways the Capture SDK can be integrated with system APIs (iOS or Android) and popular third-party libraries.

Log Forwarding

Integrate Capture SDK with one of the supported 3rd party logging libraries.The SDK provides several small side libraries that facilitate the forwarding of your existing logs to the SDK with just a few lines of code.

Timber (Android)

Forward all logs emitted with the use of Timber logging library to the Capture SDK.

Installation

Configure the bitdrift Maven repo by following the steps from Android installation guide.

Next, add the following line to the dependencies in your build.gradle file:

Kotlin
dependencies {
  implementation("io.bitdrift:capture-timber:<version>")
}
Groovy
dependencies {
  implementation 'io.bitdrift:capture-timber:<version>'
}

Usage

Kotlin
import io.bitdrift.capture.timber.CaptureTree
import timber.log.Timber
// ...
Timber.plant(CaptureTree())
Java
import io.bitdrift.capture.timber.CaptureTree;
import timber.log.Timber;
// ...
Timber.plant(new CaptureTree());

CocoaLumberjack (iOS)

Forward all logs emitted with the use of CocoaLumberjack logging library to the Capture SDK.

Installation

Swift
.package(url: "https://github.com/bitdriftlabs/capture-ios.git", from: "<version>")

Next, add the CaptureCocoaLumberjack package product to your Xcode target.

Ruby
pod "CaptureCocoaLumberjack"

Usage

Swift
import CaptureCocoaLumberjack
import CocoaLumberjackSwift
// ...

Logger
  .start(
    withAPIKey: "<your-api-key>",
    sessionStrategy: .activityBased()
  )?
  .enableIntegrations([.cocoaLumberjack()])

SwiftyBeaver (iOS)

Forward all logs emitted with the use of SwiftyBeaver logging library to the Capture SDK.

Installation

Swift
.package(url: "https://github.com/bitdriftlabs/capture-ios.git", from: "<version>")

Next, add the CaptureSwiftyBeaver package product to your Xcode target.

Ruby
pod 'BitdriftSwiftyBeaver'

Usage

Swift
import CaptureSwiftyBeaver
import SwiftyBeaver
// ...

Logger
  .start(
    withAPIKey: "<your-api-key>",
    sessionStrategy: .activityBased()
  )?
  .enableIntegrations([.swiftyBeaver()])

Linking Sessions

Developers will want to navigate from a third party view to the bitdrift Capture Timeline view to better understand what happened surrounding a crash/bug/etc. To accomplish this users can embed a link to the Capture Timeline view from the reported event using the Logger's session url getter.

In order for the sessions to be captured there should be at least one running Workflow triggering the Record Session action. The examples below assume the existence of a Workflow that matches on a log line as shown here:

Workflows Menu

Read more about how to capture sessions in Workflows here.

Note

In order to increase the chances that the error is reported in the same session, it is recommended that you use ActivityBased as your SessionStrategy when starting SDK. This is due to the libraries reporting errors on the next app launch after a crash. Read more about Capture SDK's Session Management here.

Bugsnag

Kotlin
Bugsnag.start(this, Configuration.load(this).apply {
  addOnSend(OnSendCallback { event ->
    // Attach a bitdrift session link to each error report
    event.addMetadata(
      "<tab_name>", // the "section" which is displayed as a tab in the Bugsnag dashboard
      "bitdrift_capture_session",
      Logger.sessionUrl ?: "none"
    )
    // Send a log to bitdrift in order to trigger a Workflow with this log line
    Logger.logError { "App Error Reported" }
    // Return `true` to report this error
    true
  })
})
Java
Configuration config = Configuration.load(this);
config.addOnSend(new OnSendCallback() {
  @Override
  public boolean onSend(Event event) {
    // Attach a bitdrift session link to each error report
    event.addMetadata(
      "<tab_name>", // the "section" which is displayed as a tab in the Bugsnag dashboard
      "bitdrift_capture_session",
      Logger.getSessionUrl() != null ? Logger.getSessionUrl() : "none";
    );
    // Send a log to bitdrift in order to trigger a Workflow with this log line
    Logger.logError(() -> "App Error Reported");
    // Return `true` to report this error
    return true;
  }
});
Bugsnag.start(this, config);
Swift
let config = BugsnagConfiguration.loadConfig()
config.addOnSendError { (event) -> Bool in
  // Attach a bitdrift session link to each error report
  event.addMetadata(
    Logger.sessionURL ?? "none",
    key: "bitdrift_capture_session", 
    section: "<tab_name>" // the "section" which is displayed as a tab in the Bugsnag dashboard
  )
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError("App Error Reported")
  // Return `true` to report this error
  return true
}
Bugsnag.start(with: config)

Crashlytics

Kotlin
// On app launch
// Attach a bitdrift session link to each error report
FirebaseCrashlytics.getInstance().setCustomKey(
  "bitdrift_capture_session", 
  Logger.sessionUrl ?: "none"
)
if (FirebaseCrashlytics.getInstance().didCrashOnPreviousExecution()) {
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError { "App Error Reported" }
}
Java
// On app launch
// Attach a bitdrift session link to each error report
FirebaseCrashlytics.getInstance().setCustomKey(
  "bitdrift_capture_session", 
  Logger.getSessionUrl() != null ? Logger.getSessionUrl() : "none";
);    
if (FirebaseCrashlytics.getInstance().didCrashOnPreviousExecution()) {
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError(() -> "App Error Reported");
}
Swift
// On app launch
// Attach a bitdrift session link to each error report
Crashlytics.crashlytics().setCustomValue(
  Logger.sessionURL ?? "none",
  forKey: "bitdrift_capture_session"
)
if (Crashlytics.crashlytics().didCrashDuringPreviousExecution) {
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError("App Error Reported")
}    

Instabug

Kotlin
Instabug.onReportSubmitHandler { report ->
  // Attach a bitdrift session link to each error report
  report.setUserAttribute(
    "bitdrift_capture_session", 
    Logger.sessionUrl ?: "none"
  )
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError { "App Error Reported" }
}
Java
Instabug.onReportSubmitHandler(new Report.OnReportCreatedListener() {
  @Override
  public void onReportCreated(Report report) {
    // Attach a bitdrift session link to each error report
    report.setUserAttribute(
      "bitdrift_capture_session", 
      Logger.getSessionUrl() != null ? Logger.getSessionUrl() : "none";
    );
    // Send a log to bitdrift in order to trigger a Workflow with this log line
    Logger.logError(() -> "App Error Reported");  
  }
});
Swift
Instabug.willSendReportHandler = { report in
  // Attach a bitdrift session link to each error report
  report.setUserAttribute(
    Logger.sessionURL ?? "none",
    withKey: "bitdrift_capture_session"
  )
  // Send a log to bitdrift in order to trigger a Workflow with this log line
  Logger.logError("App Error Reported")
  return report
}

Sentry

Kotlin
SentryAndroid.init(this) { options ->
  options.beforeSend = BeforeSendCallback { event, hint ->
    // Send a log to bitdrift in order to trigger a Workflow with this log line
    Logger.logError { "App Error Reported" }
    // Return event to report this error
    event
  }
}
Sentry.configureScope { scope ->
  // Attach a bitdrift session link to each error report
  scope.setContexts("bitdrift_capture_session", Logger.sessionUrl ?: "none")
}
Java
SentryAndroid.init(this, options -> {
  options.setBeforeSend((event, hint) -> {
    // Send a log to bitdrift in order to trigger a Workflow with this log line
    Logger.logError(() -> "App Error Reported");
    // Return event to report this error
    return event;
  });
});
// Attach a bitdrift session link to each error report
Sentry.configureScope(scope -> {
  scope.setContexts("bitdrift_capture_session", Logger.getSessionUrl() != null ? Logger.getSessionUrl() : "none");
});
Swift
SentrySDK.start { options in
    options.beforeSend = { event in
        // Send a log to bitdrift in order to trigger a Workflow with this log line
        Logger.logError("App Error Reported")
        // Return event to report this error
        return event
    }
}
// Attach a bitdrift session link to each error report
SentrySDK.configureScope { scope in
    scope.setContext(value: [
        "url": "Logger.sessionURL ?? "none"",
    ], key: "bitdrift_capture_session")
}    

Networking

To automatically capture logs for application HTTP network traffic, Capture provides OkHttp integration on Android and URLSession integration on iOS.

Info

The app can add the x-capture-path-template HTTP header to its requests to control the path template used to emit Capture HTTP traffic logs. The SDK uses the value of this header as a path template for the requests on which it is present.

OkHttp (Android)

Auto-Instrumentation via Gradle Plugin

The Capture Android Gradle plugin will automatically instrument all your network requests by adding the CaptureOkHttpEventListener to all of your OkHttpClient instances through bytecode manipulation.

After setting up the bitdrift maven repo as a repository in the pluginManagement block in settings.gradle, enable the capture plugin at build time in your build.gradle file:

Kotlin
plugins {
    id("io.bitdrift.capture-plugin") version "<version>"
}
Groovy
plugins {
    id 'io.bitdrift.capture-plugin' version '<version>'
}

Starting with version 0.17.28, automatic OkHttp instrumentation is disabled by default, and must be enabled via custom DSL at the bottom of your build.gradle file:

Kotlin
bitdrift {
    instrumentation {
        automaticOkHttpInstrumentation = true
    }
}
Groovy
bitdrift {
    instrumentation {
        automaticOkHttpInstrumentation = true
    }
}

Manual Instrumentation

Capture OkHttp can be enabled with one line of code using Capture's instance of okhttp3.EventListener.Factory.

Kotlin
import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory
import okhttp3.OkHttpClient

val client = OkHttpClient.Builder()
  .eventListenerFactory(CaptureOkHttpEventListenerFactory())
  .build()
Java
import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory;
import okhttp3.OkHttpClient;

OkHttpClient client = new OkHttpClient.Builder()
  .eventListenerFactory(new CaptureOkHttpEventListenerFactory())
  .build();

Capture's event listener factory can be combined with an existing instance of okhttp3.EventListener.Factory or okhttp3.EventListener.

Kotlin
// Combining `CaptureOkHttpEventListenerFactory` with an existing instance of `EventListener.Factory`.
val client = OkHttpClient.Builder()
  .eventListenerFactory(CaptureOkHttpEventListenerFactory(existingEventListenerFactory))
  .build()

// Combining `CaptureOkHttpEventListenerFactory` with an existing instance of `EventListener`.
val client = OkHttpClient.Builder()
  .eventListenerFactory(CaptureOkHttpEventListenerFactory(existingEventListener))
  .build()
Java
// Combining `CaptureOkHttpEventListenerFactory` with an existing instance of `EventListener.Factory`.
OkHttpClient client = new OkHttpClient.Builder()
  .eventListenerFactory(new CaptureOkHttpEventListenerFactory(existingEventListenerFactory))
  .build();

// Combining `CaptureOkHttpEventListenerFactory` with an existing instance of `EventListener`.
OkHttpClient client = new OkHttpClient.Builder()
  .eventListenerFactory(new CaptureOkHttpEventListenerFactory(existingEventListener))
  .build();

Apollo GraphQL

If you use apollo for GraphQL v4 you can add extra instrumentation to those calls by using the io.bitdrift.capture-apollo dependency.

Note

For v3 and lower, the OkHttp networking integration described above will do a best-effort to automatically extract the graphql operation name from the X-APOLLO-OPERATION-NAME HTTP header provided by apollo. There's no need to use this interceptor in that case.

Installation

Configure the bitdrift Maven repo by following the steps from Android installation guide.

Next, add the following line to the dependencies in your build.gradle file:

Kotlin
dependencies {
  implementation("io.bitdrift:capture-apollo:<version>")
}
Groovy
dependencies {
  implementation 'io.bitdrift:capture-apollo:<version>'
}

Usage

It is required that you are already instrumenting your OkHttp networking requests with the CaptureOkHttpEventListener as described above.

Kotlin
import com.apollographql.apollo.ApolloClient
import com.apollographql.apollo.network.okHttpClient
import io.bitdrift.capture.apollo.CaptureApolloInterceptor
import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory
import okhttp3.OkHttpClient

val client = OkHttpClient.Builder()
  // you can skip this if you're using the gradle capture-plugin
  .eventListenerFactory(CaptureOkHttpEventListenerFactory())
  .build()

val apolloClient = ApolloClient.Builder()
  .okHttpClient(okHttpClient)
  .addInterceptor(CaptureApolloInterceptor())
  .build()
Java
import com.apollographql.apollo.ApolloClient;
import com.apollographql.apollo.network.okHttpClient;
import io.bitdrift.capture.apollo.CaptureApolloInterceptor;
import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory;
import okhttp3.OkHttpClient;

OkHttpClient client = new OkHttpClient.Builder()
  // you can skip this if you're using the gradle capture-plugin
  .eventListenerFactory(new CaptureOkHttpEventListenerFactory())
  .build();

ApolloClient apolloClient = new ApolloClient.Builder()
  .httpEngine(new DefaultHttpEngine(okHttpClient))
  .addInterceptor(new CaptureApolloInterceptor())
  .build();      

URLSession (iOS)

Capture URLSession integration can be enabled with just one line of code. It's recommended that the logger is started and integrations are enabled as early in the application lifecycle as possible. If the integration is started after the app has created or accessed instances of URLSession, it may result in the SDK not emitting logs for URLSessionTasks started with those sessions.

Swift
Logger
  .start(
    withAPIKey: "<your-api-key>",
    sessionStrategy: .activityBased()
  )?
  .enableIntegrations([.urlSession()])

The above integration method uses swizzling to provide a seamless integration experience. The alternate integration method is swizzling-free and works in cases where the logger is configured and the integration is enabled after the URLSession instances are created or accessed.

Swift
Logger
  .start(
    withAPIKey: "<your-api-key>",
    sessionStrategy: .activityBased()
  )?
  .enableIntegrations([.urlSession()], disableSwizzling: true)

let session = URLSession(
  instrumentedSessionWithConfiguration: .default,
  delegate: nil,
  delegateQueue: nil
)

Apollo GraphQL

If you use apollo for GraphQL the URLSession networking integration described above will do a best-effort to automatically extract the graphql operation name from the X-APOLLO-OPERATION-NAME HTTP header provided by apollo.