Logging and Analytics
Relevant source files
The following files were used as context for generating this wiki page:
- README.md
- app/src/main/java/com/vocabri/VocabriApplication.kt
- gradle/libs.versions.toml
- app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt
- data/src/main/java/com/vocabri/data/datasource/word/WordLocalDataSourceImpl.kt
- app/src/main/java/com/vocabri/ui/screens/dictionarydetails/viewmodel/DictionaryDetailsViewModel.kt
The Vocabri application incorporates a robust system for logging, analytics, and stability monitoring. This system is crucial for both development-time debugging and production-level observability. It leverages the Kermit library for structured logging and Google's Firebase platform for analytics and crash reporting. This combination provides developers with detailed insights into the application's behavior, user interactions, and overall health.
Sources: README.md, gradle/libs.versions.toml
Architecture Overview
The logging and analytics architecture is composed of two primary components: the Kermit logging framework and the Firebase SDKs for Analytics and Crashlytics. While the README.md mentions a core/logger module, the implementation across the codebase shows a direct-use pattern of a logger() extension function, which is provided by the Kermit library. Firebase integration is configured at the project level and is ready for use throughout the application.
Sources: README.md, app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt:31, gradle/libs.versions.toml:53-54
This diagram illustrates the high-level architecture of the logging and analytics systems.
Key Components
The following table summarizes the tools used for logging and analytics in the project.
| Library | Purpose | Configuration |
|---|---|---|
| Kermit | Structured logging for development and debugging. | co.touchlab:kermit dependency. |
| Firebase Analytics | Collects usage and behavior data. | com.google.firebase:firebase-analytics dependency. |
| Firebase Crashlytics | Crash, error, and non-fatal reporting. | com.google.firebase:firebase-crashlytics dependency. |
Sources: README.md, gradle/libs.versions.toml:43,80-81
Logging with Kermit
Kermit is used for all internal application logging. It is accessed via a logger() extension function, which provides a logger instance tagged with the class name where it's called. This allows for easy filtering and identification of log sources in Logcat.
Sources: README.md, app/src/main/java/com/vocabri/VocabriApplication.kt:32, app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt:31
Initialization
Logging is available as soon as the application starts. The VocabriApplication class obtains a logger instance and logs messages during its onCreate lifecycle method, confirming that the logging system is active from the very beginning.
// app/src/main/java/com/vocabri/VocabriApplication.kt:31-48
class VocabriApplication : Application() {
private val log = logger()
override fun onCreate() {
super.onCreate()
log.i { "VocabriApplication is starting" }
// Initialize Koin
startKoin {
log.i { "Initializing Koin with application context and modules" }
androidLogger()
androidContext(this@VocabriApplication)
modules(
dataModule,
domainModule,
appModule,
)
}
log.i { "Koin initialization completed" }
}
}
Sources: app/src/main/java/com/vocabri/VocabriApplication.kt:31-48
The following sequence diagram shows the logging calls during application startup.
Usage in ViewModels
ViewModels extensively use logging to trace event handling, state changes, and asynchronous operations. This is critical for debugging UI-related logic.
// app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt:39-53
fun handleEvent(event: MainEvent) {
log.i { "Handling event: $event" }
when (event) {
is MainEvent.OnPlusButtonLongClick -> onPlusButtonLongClick()
}
}
private fun onPlusButtonLongClick() {
val word = generateRandomWordUseCase.execute()
log.d { "Word to save: $word" }
viewModelScope.launch(ioScope.coroutineContext) {
try {
addWordsUseCase.execute(word)
log.i { "Word saved successfully" }
} catch (e: IllegalStateException) {
log.e(e) { "Word already exists, ignore and leave: $e" }
} catch (e: Exception) {
log.e(e) { "Failed to save the word: $e" }
}
}
}
Sources: app/src/main/java/com/vocabri/ui/main/viewmodel/MainViewModel.kt:39-53
Usage in Data Layer
The data layer uses logging to monitor database interactions, such as insertions, queries, and deletions. This helps diagnose data persistence issues.
// data/src/main/java/com/vocabri/data/datasource/word/WordLocalDataSourceImpl.kt:40-50
override suspend fun insertWord(word: Word) {
log.i { "Starting insertWord for Word ID = ${word.id}, text = ${word.text}" }
try {
// Insert into WordBaseEntity
database.wordQueries.insertWord(
id = word.id,
text = word.text,
)
log.i { "Inserted base entity for Word ID = ${word.id}" }
} catch (e: Exception) {
log.e { "Error inserting base entity for Word ID = ${word.id}: ${e.message}" }
throw e
}
// ...
}
Sources: data/src/main/java/com/vocabri/data/datasource/word/WordLocalDataSourceImpl.kt:40-50
Analytics and Stability with Firebase
The project is configured to use Firebase Analytics and Firebase Crashlytics for monitoring the production application. This provides insight into user behavior and application stability.
Sources: README.md
Configuration
The integration is established through dependencies declared in the gradle/libs.versions.toml file and the inclusion of a google-services.json file in the app module.
The following dependencies are defined:
# gradle/libs.versions.toml:53-54
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebase-bom" }
firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" }
firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics" }
Sources: gradle/libs.versions.toml:53-54, 80-81
The README.md notes that a google-services.json file is included in the repository for convenience, but it can be replaced for a custom Firebase project.
Sources: README.md
Crash Reporting
While explicit calls to FirebaseCrashlytics are not present in the analyzed files, the Kermit logger is capable of being configured to automatically report exceptions logged at the error level (log.e(e)) to Crashlytics. This provides a seamless way to capture and analyze crashes and non-fatal exceptions.
For example, the error logging in DictionaryDetailsViewModel would likely be captured by Crashlytics in a production build.
// app/src/main/java/com/vocabri/ui/screens/dictionarydetails/viewmodel/DictionaryDetailsViewModel.kt:140-149
private fun deleteWordInternally(wordId: String) {
log.i { "Starting to delete word with id: $wordId" }
viewModelScope.launch(ioScope.coroutineContext) {
try {
deleteWordUseCase.execute(wordId)
log.i { "Word with id: $wordId deleted successfully" }
} catch (e: Exception) {
log.e(e) { "Failed to delete word with id: $wordId" }
// ... state update
}
}
}
Summary
Vocabri's logging and analytics strategy effectively separates concerns between development and production. Kermit provides detailed, structured logs that are invaluable for debugging during development. Firebase Analytics and Crashlytics offer a powerful suite of tools for monitoring the app's performance, stability, and user engagement in a production environment. This dual approach ensures developers have the right information at the right time, from tracing a single function call to understanding a widespread crash.
Sources: README.md, app/src/main/java/com/vocabri/VocabriApplication.kt