0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AndroidStudio: Timber でログ出力をファイルにする

Last updated at Posted at 2023-11-23

こちらと同じことを行いました。
Androidアプリにログファイル出力機能を実装する方法

ツリー構造
image.png

環境

AndroidManifest.xml
(省略)
<application
    android:name=".LoggingApp"
(省略)
app/build.gradle.kts
(省略)
dependencies {
  implementation("com.jakewharton.timber:timber:5.0.1")
(省略)

プログラム

MainActivity.kt
package org.ekzemplaro

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import timber.log.Timber

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        Timber.d("*** onCreate ***")
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        Timber.d("*** onStart ***")
        super.onStart()
    }

    override fun onResume() {
        Timber.d("*** onResume ***")
        super.onResume()
    }

    override fun onPause() {
        Timber.d("*** onPause ***")
        super.onPause()
    }

    override fun onStop() {
        Timber.d("*** onStop ***")
        super.onStop()
    }

    override fun onDestroy() {
        Timber.d("*** onDestroy ***")
        super.onDestroy()
    }
}
LoggingApp.kt
package org.ekzemplaro

import android.app.Application
import android.content.Context
import timber.log.Timber


class LoggingApp: Application() {
    override fun onCreate() {
        super.onCreate()

        Timber.plant(LogTree(context = this))
    }

    class LogTree(private val context: Context): Timber.DebugTree() {
        // 以下については後のステップで説明します。
        override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
            LogFile().postLog(context, message)
        }
    }
}
LogFile.kt
package org.ekzemplaro

import android.content.Context
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors


class LogFile {
    companion object {
        private val executor: ExecutorService = Executors.newSingleThreadExecutor()
        const val LOG_EXPIRED_DAY = 13
    }

    fun postLog(context: Context, message: String) {
        createLogLine(message).let {
            executor.takeUnless { it.isShutdown }?.execute{
                flush(context, it)
            }
        }
    }

    private fun createLogLine(
        message: String,
        date: LocalDateTime = LocalDateTime.now()
    ): String {
        return  "${date.format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"))} $message"
    }

    private fun flush(context: Context, log: String) {
        val today = LocalDateTime.now()
        val fileName =
            "${today.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))}.log"
        val file = File(context.filesDir, fileName)

        // まだ当日分のファイルが作成されていなかったら
        // 14日前までに作成されたファイルを削除する
        if (!file.exists()) deleteExpiredFiles(context)

        BufferedWriter(FileWriter(file, true)).use { writer ->
            writer.write(log)
            writer.newLine()
            writer.flush()
        }
    }

    private fun deleteExpiredFiles(context: Context) {
        context.filesDir
            .listFiles()
            ?.toList()
            ?.filter { file -> file.name.endsWith(".log") }
            ?.let { fileList ->
                fileList.map { file ->
                    if (expired(file) && file.exists()) file.delete()
                }
            }
    }

    private fun expired(file: File): Boolean {
        return toLocalDate(file.lastModified())
            .isBefore(LocalDate.now().minusDays(LOG_EXPIRED_DAY.toLong()))
    }
    private fun toLocalDate(lastModified: Long): LocalDate {
        return Instant
            .ofEpochMilli(lastModified)
            .atZone(ZoneId.systemDefault())
            .toLocalDate()
    }
}

実行結果

確認方法

adb -s emulator-5554 shell
$ adb -s emulator-5554 shell
emu64xa:/ $ su
emu64xa:/ # cd data/data/org.ekzemplaro/files
emu64xa:/data/data/org.ekzemplaro/files # ls
2023-11-23.log  profileInstalled
emu64xa:/data/data/org.ekzemplaro/files # cat 2023-11-23.log

作成されたファイル

2023-11-23.log
2023/11/23 17:15:57 *** onCreate ***
2023/11/23 17:15:57 *** onStart ***
2023/11/23 17:15:57 *** onResume ***
2023/11/23 17:16:31 *** onCreate ***
2023/11/23 17:16:32 *** onStart ***
2023/11/23 17:16:32 *** onResume ***

実機でテストした場合

実機を開発者モードにして、USBケーブルで接続します。

$ adb devices
List of devices attached
123456789A	device
emulator-5554	device

$ adb -s 123456789A shell
rk322x_box:/ $ su
rk322x_box:/ # cd data/data/org.ekzemplaro/files
rk322x_box:/data/data/org.ekzemplaro/files # ls
2023-12-05.log
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?