Remote logging with Timber and Firebase Realtime Database — статья о том, как хранить логи приложения на удаленном сервере без необходимости поднимать собственный сервер.
Автору было необходимо каким-то образом получать логи от пользователей, при том что среднестатистический пользователь в принципе не знает, как снять логи, а включать логирование в релизных сборках — не самая лучшая идея. Выход нашелся в использовании облачной Firebase Realtime Database и библиотеки логирования Timber.
РЕКОМЕНДУЕМ:
Хорошие и плохие приемы программирования на Kotlin
Хранение логов в Firebase
Для начала надо подключить библиотеку Firebase к приложению, а затем зарегистрироваться и создать проект в Firebase Console. Об этом написано множество туториалов.
С помощью консоли Firebase нужно создать новую базу данных и добавить правила доступа:
1 2 3 4 5 6 |
{ "rules": { ".read": true, ".write": true } } |
Далее к проекту следует подключить библиотеку Timber:
1 |
implementation 'com.jakewharton.timber:timber:4.7.1' |
Также понадобится data-класс для хранения записей логов:
1 2 3 4 5 6 7 |
data class RemoteLog( var priority: String, var tag: String?, var message: String, var throwable: String?, val time : String ) |
И класс для хранения информации об устройстве:
1 2 3 4 5 6 7 8 9 10 |
data class DeviceDetails( val deviceId: String, val osVersion: String = Build.VERSION.RELEASE, val manufacturer: String = Build.MANUFACTURER, val brand: String = Build.BRAND, val device: String = Build.DEVICE, val model: String = Build.MODEL, val appVersionName: String = BuildConfig.VERSION_NAME, val appVersionCode: Int = BuildConfig.VERSION_CODE ) |
Для создания записей на основе этого класса воспользуемся следующим деревом Timber (в терминологии Timber это своего рода хендлер, обработчик записываемых в лог сообщений):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class TimberRemoteTree(private val deviceDetails: DeviceDetails) : Timber.DebugTree() { private val dateFormat = SimpleDateFormat("dd-MM-yyyy", Locale.getDefault()) private val timeFormat = SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS a zzz", Locale.getDefault()) private val date = dateFormat.format(Date(System.currentTimeMillis())) private val logRef = Firebase.database.getReference("logs/$date/${deviceDetails.deviceId}") override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { if (BuildConfig.REMOTE_LOG_ENABLED) { val timestamp = System.currentTimeMillis() val time = timeFormat.format(Date(timestamp)) val remoteLog = RemoteLog(priorityAsString(priority), tag, message, t.toString(), time) with(logRef) { updateChildren(mapOf(Pair("-DeviceDetails", deviceDetails))) child(timestamp.toString()).setValue(remoteLog) } } else super.log(priority, tag, message, t) } private fun priorityAsString(priority: Int): String = when (priority) { Log.VERBOSE -> "VERBOSE" Log.DEBUG -> "DEBUG" Log.INFO -> "INFO" Log.WARN -> "WARN" Log.ERROR -> "ERROR" Log.ASSERT -> "ASSERT" else -> priority.toString() } } |
Это все, теперь достаточно «посадить» это дерево:
1 2 3 4 5 6 7 8 |
if (BuildConfig.DEBUG) { val deviceId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) val deviceDetails = DeviceDetails(deviceId) val remoteTree = TimberRemoteTree(deviceDetails) Timber.plant(remoteTree) } else { // TODO plant timber release tree. } |
РЕКОМЕНДУЕМ:
Переключение окружения в Firebase
Как видно, облачное сохранение логов будет активировано только в том случае, если это DEBUG-сборка приложения, а переменная BuildConfig.REMOTE_LOG_ENABLED имеет значение true.