Android Custom Events (Deprecated)
Custom Events are deprecated, replaced by
High Resolution Telemetry (HRT)
and the new Event
type, and
collected using the Reporting library.
HRT Events preserve the primary purpose of Custom Events (viewing individual events on the device timeline), while also allowing aggregate metrics to be generated from the Events.
HRT is supported from Android SDK 4.5.0 onwards. Please use HRT instead of Custom Events moving forward. In a future release SDK release, all Custom Events may be processed as Metrics instead, or the API may be removed entirely.
Custom Events were previously called Structured Logs. This API was changed in Bort 3.6.0.
Any code using the StructuredLog
or CustomEvent
APIs from the
structured-log-lib
or custom-event-lib
libraries (now removed) must be
changed to call the equivalent methods in the CustomEvent
API from the
reporting-lib
library, as described below.
When diagnosing Android device issues, logcat logs are one of the most common sources of information to use. Unfortunately, the signal-to-noise ratio can be poor. On top of that, logcat prunes logs from chatty sources, causing important logs to get lost.
The Custom Events feature is a complementary mechanism to log events of interest for debugging purposes. Your application and systems code can leverage this to log crucial information and get it surfaced in the Memfault UI.
Custom Events appear on the Device Timeline. For each type of log, a dedicated swimlane is created automatically. Each log is represented with a dot. Clicking or hovering over it will reveal the logged JSON data:
Overview
Custom Events are collected by the MemfaultStructuredLogd daemon that is built & installed automatically as part of the Memfault Android SDK. The daemon exposes a Binder service that log producers can use to send Custom Events to the daemon. For ease of use, the SDK also includes a small, Java library that exposes a simple API (see Logging API below). The MemfaultStructuredLogd daemon buffers received logs to disk until it is required to flush all pending logs to the Bort app (MemfaultBort.apk). This flushing happens periodically, when the buffer has reached a certain size limit or when requested explicitly (see Testing & Debugging). Finally, the Bort app is responsible for batch-uploading the collected logs to the Memfault web service.
Each Custom Event has a "type" (string) and contains structured data in the form of a JSON payload. The schema of the JSON payload is up to you. That said, we recommend using the same schema for a given type. Future aggregation features may require this.
Custom Events vs Logcat
Custom Events | Logcat | |
---|---|---|
Log Viewing UX | Each log appears individually on the Device Timeline | Logs are viewed in a view separate from the Device Timeline |
Log Payload | JSON | Text |
Log Metadata | Timestamp, type | Timestamp, log level, tag, user, PID, TID |
Log Producers | Only subsystems you care about | Everything |
Intended Log Frequency | Low-Medium (10s per hour) | High (1000s per hour) |
Rate Limiting | Logs get dropped if the rate exceeds threshold (1000/hr) | Logs from "chatty" sources get dropped to make space for new logs |
Maximum Payload Size per Log | 4KB | 4KB |
Backing Storage | Disk | RAM, lost upon reboot (Disk is optional) |
Ingestion of Custom Events may be rate-limited. Avoid sending more than 1000 custom events per hour per device.
Adding Custom Events
Tapping into the Custom Events feature, boils down to these steps:
- Defining the log types and data payloads your applications and services need to log
- Add the
reporting-lib
library to the component from which you want to log - Call the
CustomEvent.log(...)
API - Test & Debug
Defining Custom Event types and data payloads
Both the "type" and JSON data payload are free-form in principle. That said, we recommend the following to get the most out of Custom Events:
- Keep the "schema" of a JSON data payload the same for all logs of a given type. Future aggregation features may require this.
- Group different but related types together by prefixing the type string with
the name of the group. We recommend using a period as separator. For example,
"motor.start"
and"motor.stop"
. - Each type of log is visualized in a swimlane of its own. Keep this in mind when structuring your log types.
- Only log for important events, errors, etc. Logs are visualized on the Device Timeline. When logging too much, all the time, the visualization breaks down quickly.
- Type strings should be statically defined in your code. Do not create type strings dynamically at run-time.
Add the reporting-lib
library
The reporting-lib
library is a small Java library that wraps the Binder
service that is published by MemfaultStructuredLogd. The next two sections
describe how to add this library to an AOSP-built Java app and how to add it to
a Gradle-built Java or Kotlin app.
Note it is possible to log from native C/C++ programs, but we do not have a C/C++ helper library at the moment.
Add reporting-lib
to AOSP-built Java apps
Make sure the Android SDK has been integrated with your AOSP build first.
Once integrated, a target called memfault-reporting-lib
will be available.
This is a static Java library that will need to be linked to your app's build
target.
If you are using an Android.mk file, add this to the
LOCAL_STATIC_JAVA_LIBRARIES
variable in your app's Android.mk file:
LOCAL_STATIC_JAVA_LIBRARIES := \
memfault-reporting-lib \
Or if you are using an Android.bp file, add memfault-reporting-lib
to a
static_libs
clause of your build target:
static_libs: [
"memfault-reporting-lib"
]
Add reporting-lib
to Gradle-built, Java or Kotlin apps
For apps that are build outside of the AOSP build system using Gradle, the
easiest way to include the reporting-lib
published on Maven Central.
Ensure that Maven Central is included in your project's root build.gradle
file:
repositories {
mavenCentral()
...
}
Then in your app's build.gradle
file, add:
dependencies {
implementation 'com.memfault.bort:reporting-lib:1.1'
...
}
When you build your project, the reporting-lib
library will be built as well
and can now be used from your app's Java or Kotlin source files.
Logging API
import com.memfault.bort.customevent.CustomEvent;
// Create a Custom Event
// with type "door.open" and JSON payload: { "foo": 123, "bar": true }
CustomEvent.log("door.open", "{\"foo\": 123, \"bar\": true}");
As you can see from the snippet above, the API is extremely simple. The first argument is the type of the Custom Event and is a string.
The second argument is the data payload and must be a string containing valid JSON, of up to 4KB in size.
For the sake of simplicity of this example, we hand-coded the JSON string. In real projects, we recommend using a JSON serialization library.
In case the data was invalid JSON or exceeded the 4KB limit, "Invalid Data" and respectively "Oversized Data" swimlanes will appear in the Device Timeline, containing information to help debug these issues.
Testing & Debugging
By default, the MemfaultStructuredLogd daemon only flushes out received logs periodically or when the configured maximum log count has been reached. During development, this is not very practical. Therefore, on debug builds of AOSP, you can also programmatically trigger it to flush out received logs:
import com.memfault.bort.customevent.CustomEvent;
// Asks MemfaultStructuredLogd to flush out pending logs:
CustomEvent.dump();
Note that the CustomEvent.dump()
API is only available on debug builds of AOSP
and it is a no-op in user builds.
MemfaultStructuredLogd writes the pending logs to a file and passes it to Android's DropBoxManager subsystem. The MemfaultBort.apk app then picks it up from the DropBoxManager and uploads it to Memfault's web service.
During debugging, it can also be useful to inspect the files in Android's DropBoxManager subsystem directly. You can do this through a root adb shell:
$ adb root
$ adb shell
# ls /data/system/dropbox/ | grep memfault
memfault_structured@1619774084281.dat
memfault_structured@1619774633397.dat
memfault_structured@1619774689613.dat
memfault_structured@1619775481075.dat
# cat /data/system/dropbox/memfault_structured@1619775481075.dat
... JSON dump of the file ...