Library

remote-logger

A Timber Tree that writes NDJSON logs to local files, plus a WorkManager worker that zips and uploads them to Firebase Cloud Storage when a dump_logs push arrives. Pull logs from any installed device without asking the user to send them.

Coordinates: com.rohittp.dependables:remote-logger:0.1.0 minSdk 24 JVM 21

Add the dependency

build.gradle.kts
dependencies {
    implementation("com.rohittp.dependables:remote-logger:0.1.0")
}

The library transitively brings timber, androidx.work, firebase-storage-ktx and firebase-messaging-ktx. You must initialise Firebase yourself (apply the com.google.gms.google-services plugin and drop in a google-services.json).

In your Application

App.kt
override fun onCreate() {
    super.onCreate()
    FirebaseApp.initializeApp(this)
    RemoteLogger.init(this)
    RemoteLogger.subscribeForDevice(this, androidId())
}

In your FirebaseMessagingService

YourFcmService.kt
override fun onMessageReceived(msg: RemoteMessage) {
    if (RemoteLogger.handleMessage(applicationContext, msg.data)) return
    // ... fall through to your own routing
}

The device id is derived from Widevine DRM with a Build-fingerprint fallback — no ANDROID_ID, no UUID.randomUUID() persistence. Need to log it elsewhere?

Anywhere.kt
val id = DeviceId.get()     // stable, FCM-topic-safe
val name = DeviceId.name() // "Samsung SM-G991B" etc.

Send an FCM data message

Target the device by topic — logdump_<deviceId>. The library subscribes for you in subscribeForDevice.

FCM payload
{
  "to": "/topics/logdump_<deviceId>",
  "data": {
    "type":  "dump_logs",
    "hours": "24"
  }
}

The worker zips databases/, shared_prefs/, datastore/, and every recent logs/<day>/<file>, plus a meta.json envelope identifying the build, and uploads to user/<deviceId>/logs/<utc-yyyyMMdd_HHmmss>.zip.

Same zip, any OutputStream

ExportZipWriter.writeTo is the same call the upload worker makes — useful for an in-app "export my data" button that writes to MediaStore.Downloads:

ExportToDownloads.kt
val exported = outputStream.use { out ->
    ExportZipWriter.writeTo(context, out, logHours = 72)
}

Override defaults

App.kt
RemoteLogger.configure(
    RemoteLoggerConfig(
        storagePathBuilder = { id, ts -> "logs/$id/$ts.zip" },
        onDeviceRetentionDays = 14,
        defaultDumpHours = 48,
    )
)
RemoteLogger.init(this)

Where logs live

filesDir/logs/
    2026-05-26/
        14-main.ndjson      one line per Timber call, INFO+
        14-export.ndjson    other processes write to their own files
        15-main.ndjson
    2026-05-25/
        ...

Day directories older than onDeviceRetentionDays (default 7) are deleted on the next RemoteLogger.init in the main process.