Mobile Observability Internals is a deep dive into the mechanisms and tools that power observability in mobile applications. It focuses on strategies to effectively monitor and diagnose the health, performance, and reliability of mobile apps. This includes collecting and analyzing a vast array of data from various points within the application lifecycle, such as error reporting, user interactions, system events, network requests, and app performance metrics. The goal is to provide insights into the app’s operational aspects to ensure optimal user experience, identify bottlenecks, and continuously improve the app based on real-world usage. This knowledge base is crucial for developers, QA teams, and operation managers who aim to maintain high standards of app quality and performance.
4. Motivation
●Improve the ease and efficiency of conducting app
audits for developers
●Accelerate the process of obtaining performance
evaluations of the application
●Automation of the audit process, reducing the time
and effort required to conduct an audit
5. Abilities
●Have an ability to capture memory leaks
●Have an ability to capture thread and virtual
machine violations
●Have an ability to capture HTTP API calls
●Have an ability to capture TTML, TTFL, TTI, Frozen,
and Slow Frames
●Have an ability to capture crashes
8. How we design app audit sdk
dependencies {
// AppAudit
implementation projectOrArtifact(':app-audit')
// to check variants debug, integration, and nightly
def buildVariant = ...
if (buildVariant.isConsideredAsIntegration()) {
implementation projectOrArtifact(':app-audit-plugin-memory-leak')
implementation projectOrArtifact(':app-audit-plugin-strict-mode')
implementation projectOrArtifact(':app-audit-plugin-page-performance')
implementation projectOrArtifact(':app-audit-plugin-network-instrumentation')
} else {
implementation projectOrArtifact(':app-audit-plugin-page-performance')
}
}
9. How we design app audit sdk
dependencies {
// AppAudit
implementation projectOrArtifact(':app-audit')
// add additional plugins and sink only on debug, integration, and nightly
def buildVariant = ...
if (buildVariant.isConsideredAsIntegration()) {
implementation projectOrArtifact(':app-audit-plugin-memory-leak')
implementation projectOrArtifact(':app-audit-plugin-strict-mode')
implementation projectOrArtifact(':app-audit-plugin-page-performance')
implementation projectOrArtifact(':app-audit-plugin-network-instrumentation')
} else {
implementation projectOrArtifact(':app-audit-plugin-page-performance')
}
}
10. How we design app audit sdk
dependencies {
// AppAudit
implementation projectOrArtifact(':app-audit')
// add additional plugins and sink only on debug, integration, and nightly
def buildVariant = ...
if (buildVariant.isConsideredAsIntegration()) {
implementation projectOrArtifact(':app-audit-plugin-memory-leak')
implementation projectOrArtifact(':app-audit-plugin-strict-mode')
implementation projectOrArtifact(':app-audit-plugin-page-performance')
implementation projectOrArtifact(':app-audit-plugin-network-instrumentation')
} else {
implementation projectOrArtifact(':app-audit-plugin-page-performance')
}
}
11. How we design app audit sdk
dependencies {
// AppAudit
implementation projectOrArtifact(':app-audit')
// add additional plugins and sink only on debug, integration, and nightly
def buildVariant = ...
if (buildVariant.isConsideredAsIntegration()) {
implementation projectOrArtifact(':app-audit-plugin-memory-leak')
implementation projectOrArtifact(':app-audit-plugin-strict-mode')
implementation projectOrArtifact(':app-audit-plugin-page-performance')
implementation projectOrArtifact(':app-audit-plugin-network-instrumentation')
} else {
implementation projectOrArtifact(':app-audit-plugin-page-performance')
}
}
12. How we design app audit sdk
class AppAuditInitializer {
fun install() {
AppAudit(
app = app,
metadata = metadata,
plugins = getPlugins(),
executor =
app.getAppScope.coroutineContext.toExecutor()
)
.install()
}
...
}
13. How we design app audit sdk
class AppAuditInitializer {
...
private fun getPlugins(): Set<AppAuditPlugin> {
val plugins = mutableSetOf<AppAuditPlugin>()
if (BuildConfig.IS_PAGE_PERFORMANCE_ENABLED) {
plugins.add(PagePerformancePlugin())
}
...
return plugins
}
}
14. How we design app audit sdk
class AppAuditInitializer {
...
private fun getPlugins(): Set<AppAuditPlugin> {
val plugins = mutableSetOf<AppAuditPlugin>()
if (BuildConfig.IS_PAGE_PERFORMANCE_ENABLED) {
plugins.add(PagePerformancePlugin(
setOf(AbcTelemetryEngine(), XyzTelemetryEngine())
))
}
...
return plugins
}
}
15. How we design page performance
●Able to capture TTFL (time to first layout)
●Able to capture TTI (time to interactive)
●Able to capture TTML (time to meaningful layout)
22. How we design app audit sdk
class AppAuditInitializer {
...
private fun getPlugins(): Set<AppAuditPlugin> {
val plugins = mutableSetOf<AppAuditPlugin>()
if (BuildConfig.IS_PAGE_PERFORMANCE_ENABLED) {
plugins.add(PagePerformancePlugin(
setOf(AbcTelemetryEngine(), XyzTelemetryEngine())
))
}
...
return plugins
}
}
23. How we design app audit sdk
class PagePerformancePlugin {
...
internal fun reporting(record: Record) {
plugin.reportEngines?.forEach { engine ->
audit.executor.execute {
engine.execute(record)
}
}
}
}
24.
25. Key takeaway
●At the Gojek level, basic page performance metrics
weren’t sufficient, hence we built an in-house, end-
to-end solution to accommodate our needs.
●Mobile observability is a method through which we
can confidently manage each release of the app.
●Mobile observability not only helps us capture
performance in the wild but also allows us to utilize
the same metrics in the development environment
(shift-left approach).