Privacy and breadcrumbs
Breadcrumb filtering is new in 7.0.0.
What breadcrumbs are
Breadcrumbs are short structured records of what the app was doing in the lead-up to an issue — navigation, taps, lifecycle changes, network requests, system events. They are aggregated into the report timeline and surface in the Bugsee dashboard.
The SDK ships four built-in breadcrumb sources:
| Source | Examples |
|---|---|
| App | Application lifecycle (onCreate, foreground/background), low-memory warnings. |
| UI | Activity / Fragment navigation, dialog open/close, gesture events. |
| Network | HTTP request lifecycle entries — host, path, method, status, mechanism. |
| System | Connectivity, battery, thermal, orientation, configuration changes. |
Breadcrumb capture is on by default and is gated by the CaptureBreadcrumbs option — see configuration.
Disabling breadcrumb collection
To turn off breadcrumb capture entirely, set CaptureBreadcrumbs to false in your manifest or programmatically:
<meta-data android:name="com.bugsee.option.capture.breadcrumbs"
android:value="false" />
Most apps should keep capture on and use the filter below to scrub or drop only the entries that matter.
Filtering breadcrumbs
Bugsee.setBreadcrumbFilter(EventFilter<Breadcrumb>) registers a single filter that is invoked for every breadcrumb the SDK is about to record — regardless of source. Pass the (possibly mutated) breadcrumb to the supplied callback to keep it, or pass null to drop it.
The filter shape mirrors setLogEventFilter and setNetworkEventFilter — the same generic EventFilter<T> interface, whose single filter(event, callback) method hands you the event and a Callback1 to invoke with the result.
HTTP-request breadcrumb URLs are emitted verbatim; opt into masking, PII redaction, or drop semantics through this filter (see the URL-masking example below).
Breadcrumb shape
The Breadcrumb interface (com.bugsee.library.contracts.exchange.Breadcrumb) exposes:
| Method | Purpose |
|---|---|
getCategory() / setCategory(String) | Source category — typical values: "navigation", "user", "system", "business", "error". |
getType() / setType(String) | Sub-type within the category (for example http_request, gesture, lifecycle). |
getMessage() / setMessage(String) | Human-readable description. |
getLevel() / setLevel(Breadcrumb.Level) | One of DEBUG, INFO, WARNING, ERROR, FATAL. |
getData() / setData(Map) / setData(key, value) | Free-form key/value context map. HTTP-request breadcrumbs put url, method, status_code, mechanism, etc. here. |
setTimestamp(long) | Wall-clock milliseconds. |
Examples
Mask query-string tokens on HTTP-request breadcrumbs
- Java
- Kotlin
import com.bugsee.library.Bugsee;
import com.bugsee.library.contracts.exchange.Breadcrumb;
Bugsee.setBreadcrumbFilter((crumb, callback) -> {
Map<String, Object> data = crumb.getData();
if (data != null) {
Object urlObj = data.get("url");
if (urlObj instanceof String) {
String url = (String) urlObj;
if (url.contains("token=")) {
crumb.setData("url",
url.replaceAll("token=[^&\\s]+", "token=[REDACTED]"));
}
}
}
callback.run(crumb); // callback.run(null) to drop the breadcrumb entirely
});
import com.bugsee.library.Bugsee
Bugsee.setBreadcrumbFilter { crumb, callback ->
val url = crumb.data?.get("url") as? String
if (url != null && url.contains("token=")) {
crumb.setData("url", url.replace(Regex("token=[^&\\s]+"), "token=[REDACTED]"))
}
callback.run(crumb) // callback.run(null) to drop the breadcrumb entirely
}
Drop noisy breadcrumb categories
- Java
- Kotlin
Bugsee.setBreadcrumbFilter((crumb, callback) -> {
if ("system".equals(crumb.getCategory())
&& "battery".equals(crumb.getType())) {
callback.run(null); // drop battery-state pings
return;
}
callback.run(crumb);
});
Bugsee.setBreadcrumbFilter { crumb, callback ->
if (crumb.category == "system" && crumb.type == "battery") {
callback.run(null) // drop battery-state pings
} else {
callback.run(crumb)
}
}
Downgrade level for known-noisy entries
- Java
- Kotlin
Bugsee.setBreadcrumbFilter((crumb, callback) -> {
String message = crumb.getMessage();
if (message != null && message.startsWith("[heartbeat]")) {
crumb.setLevel(Breadcrumb.Level.DEBUG);
}
callback.run(crumb);
});
Bugsee.setBreadcrumbFilter { crumb, callback ->
if (crumb.message?.startsWith("[heartbeat]") == true) {
crumb.level = Breadcrumb.Level.DEBUG
}
callback.run(crumb)
}
Declaring the filter in the manifest
You can also point Bugsee at a breadcrumb filter from AndroidManifest.xml,
with no code. Add a com.bugsee.filter.breadcrumb <meta-data> whose value is
a class that implements EventFilter<Breadcrumb> and has a public no-arg
constructor. The benefit: the filter is in force from the very first captured
event, even under auto-initialization, before any of your own code runs.
<application>
<meta-data android:name="com.bugsee.filter.breadcrumb"
android:value="com.example.MyBreadcrumbFilter" />
</application>
- Java
- Kotlin
import androidx.annotation.Keep;
import com.bugsee.library.contracts.common.Callback1;
import com.bugsee.library.contracts.exchange.Breadcrumb;
import com.bugsee.library.contracts.exchange.EventFilter;
@Keep // referenced only by name in the manifest — keep it from R8
public class MyBreadcrumbFilter implements EventFilter<Breadcrumb> {
public MyBreadcrumbFilter() { } // required public no-arg constructor
@Override
public void filter(Breadcrumb crumb, Callback1<Breadcrumb> callback) {
if ("system".equals(crumb.getCategory())
&& "battery".equals(crumb.getType())) {
callback.run(null); // drop battery-state pings
return;
}
callback.run(crumb);
}
}
import androidx.annotation.Keep
import com.bugsee.library.contracts.common.Callback1
import com.bugsee.library.contracts.exchange.Breadcrumb
import com.bugsee.library.contracts.exchange.EventFilter
@Keep // referenced only by name in the manifest — keep it from R8
class MyBreadcrumbFilter : EventFilter<Breadcrumb> {
override fun filter(crumb: Breadcrumb, callback: Callback1<Breadcrumb>) {
if (crumb.category == "system" && crumb.type == "battery") {
callback.run(null) // drop battery-state pings
} else {
callback.run(crumb)
}
}
}
A filter set in code with Bugsee.setBreadcrumbFilter(...) takes precedence
over the manifest one. Because the class is referenced only by name, keep it
from R8 — annotate it @Keep or add -keep class com.example.MyBreadcrumbFilter { <init>(); }.
The same mechanism is available for network events and logs.
Recording custom breadcrumbs
Beyond the built-in sources, you can record your own breadcrumbs with Bugsee.addBreadcrumb(...) — see Breadcrumbs → Recording custom breadcrumbs. Custom breadcrumbs run through the filter you register here, exactly like the built-in ones.
Notes
- The filter is synchronous — it runs on the recording thread before the breadcrumb is persisted. Keep it allocation-light and fast; do not perform I/O.
- Mutate the breadcrumb in place and pass the same instance to
callback.run(...). Passing a differentBreadcrumbimplementation to the callback is not supported. - The filter is invoked for all sources. Use
getCategory()/getType()to scope your logic per source (for example, only touch HTTP-request entries by checkingcategory == "navigation"andtype == "http_request"). - Network events (the entries surfaced in the network tab of the report) are scrubbed separately via
setNetworkEventFilter. The breadcrumb filter only affects the breadcrumb timeline.
Related
- Console logs filter —
setLogEventFilter. - Network traffic filter —
setNetworkEventFilter. - Configuration:
CaptureBreadcrumbs— master toggle for breadcrumb capture.