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:
dependencies {
implementation("io.bitdrift:capture-timber:<version>")
}
dependencies {
implementation 'io.bitdrift:capture-timber:<version>'
}
Usage¶
import io.bitdrift.capture.timber.CaptureTree
import timber.log.Timber
// ...
Timber.plant(CaptureTree())
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¶
.package(url: "https://github.com/bitdriftlabs/capture-ios.git", from: "<version>")
Next, add the CaptureCocoaLumberjack
package product to your Xcode target.
pod "CaptureCocoaLumberjack"
Usage¶
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¶
.package(url: "https://github.com/bitdriftlabs/capture-ios.git", from: "<version>")
Next, add the CaptureSwiftyBeaver
package product to your Xcode target.
pod 'BitdriftSwiftyBeaver'
Usage¶
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:
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¶
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
})
})
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);
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¶
// 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" }
}
// 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");
}
// 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¶
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" }
}
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");
}
});
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¶
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")
}
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");
});
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.
- Refer to the Product Guides > Networking Insights section to learn on how the product can surface visualizations of this data.
- Refer to the SDK > HTTP Traffic Logs section to learn more about how the format the Capture SDK uses to emit these logs.
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:
plugins {
id("io.bitdrift.capture-plugin") version "<version>"
}
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:
bitdrift {
instrumentation {
automaticOkHttpInstrumentation = true
}
}
bitdrift {
instrumentation {
automaticOkHttpInstrumentation = true
}
}
Manual Instrumentation¶
Capture OkHttp
can be enabled with one line of code using Capture's instance of okhttp3.EventListener.Factory
.
import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory
import okhttp3.OkHttpClient
val client = OkHttpClient.Builder()
.eventListenerFactory(CaptureOkHttpEventListenerFactory())
.build()
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
.
// 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()
// 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:
dependencies {
implementation("io.bitdrift:capture-apollo:<version>")
}
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.
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()
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 URLSessionTask
s started with those sessions.
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.
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.