自己封裝安卓日志打印庫

Logger庫說明文檔

在日常開發(fā)中,日志收集是個(gè)非常重要的功能,很多時(shí)候,我們都是簡(jiǎn)單的使用Log.d直接打印我們想要的內(nèi)容,這樣開發(fā)當(dāng)然沒問題。包括說簡(jiǎn)單封裝下,統(tǒng)一到一個(gè)接口方法打印,也是很常見的手法。我只是在這個(gè)基礎(chǔ)上面,抽取個(gè)簡(jiǎn)單實(shí)現(xiàn)的庫而已,沒啥特別的。

用法

初始化

    private fun initLogger() {
        //設(shè)置上下文
        FastLogger.initLogger(this)
        //控制是否打印
        FastLogger.logEnable = true
        //控制是否保存到日志文件
        FastLogger.logSave2File = true
        //自定義打印
        FastLogger.setInterceptor(object : AbsLogInterceptChain() {
            override fun intercept(
                priority: Int,
                tag: String,
                logMsg: String?,
                throwable: Throwable?
            ) {
                //log text
            }
        })
    }

打印

        FastLogger.i(TAG,"this is info level log")
        FastLogger.d(TAG,"this is debug level log")
        FastLogger.w(TAG,"this is warn level log")
        FastLogger.e(TAG,"this is error level log",NullPointerException("this is NullPointerException"))

實(shí)現(xiàn)思路

整體實(shí)現(xiàn)思路用到了設(shè)計(jì)模式中的:責(zé)任鏈模式。(假裝牛逼)

語言使用的是 Kotlin。

第一步:定義對(duì)外接口

首先我們定義我們對(duì)外的對(duì)象類 FastLogger,并且定義出我們對(duì)外的方法

object FastLogger {
        fun d(tag: String, message: String) {
    }

    fun e(tag: String, message: String, throwable: Throwable? = null) {
    }

    fun w(tag: String, message: String) {
    }

    fun i(tag: String, message: String) {
    }
}

這是四個(gè)我們常見的打印方法,我們的所有邏輯都是圍繞這四個(gè)方法進(jìn)行開發(fā)的。

第二步:實(shí)現(xiàn)打印方法

在實(shí)現(xiàn)這個(gè)方法之前,我們跟一下官方打印的調(diào)用流程:

android.util.Log

    public static int i(String tag, String msg) {
        return println(LOG_ID_MAIN, INFO, tag, msg);
    }
    public static int d(String tag, String msg) {
        return println(LOG_ID_MAIN, DEBUG, tag, msg);
    }   
    public static int w(String tag, String msg) {
        return println(LOG_ID_MAIN, WARN, tag, msg);
    }    
    public static int e(String tag, String msg, Throwable tr) {
        return println(LOG_ID_MAIN, ERROR, tag, msg + '\n' + getStackTraceString(tr));
    }

可以看到,最終都是調(diào)用的println方法

    public static int println(int bufID,int priority, String tag, String msg)

那么最終,其實(shí)就是需要以下幾個(gè)參數(shù):

  • priority:日志級(jí)別
  • tag:TAG
  • msg:日志內(nèi)容

所以,我基于此,定義我們的打印方法,并且通過一個(gè)變量控制日志是否可以打印。

object FastLogger {

    var logEnable = BuildConfig.DEBUG

    fun d(tag: String, message: String) {
        log(Log.DEBUG, message, tag)
    }

    fun e(tag: String, message: String, throwable: Throwable? = null) {
        log(Log.ERROR, message, tag, throwable = throwable)
    }

    fun w(tag: String, message: String) {
        log(Log.WARN, message, tag)
    }

    fun i(tag: String, message: String) {
        log(Log.INFO, message, tag)
    }

    @Synchronized
    private fun log(
        priority: Int,
        message: String,
        tag: String,
        throwable: Throwable? = null
    ) {
        if (logEnable) {
            Log.println(priority, message, tag + '\n' + Log.getStackTraceString(throwable));
        }
    }
}

在這一步,其實(shí),我們一個(gè)打印庫就封裝好了。

只是在這個(gè)基礎(chǔ)上面,我們實(shí)現(xiàn)的和官方提供的沒啥區(qū)別,就是加了個(gè)開關(guān)控制而已。我們應(yīng)該對(duì)其美化一下。

第三步:使用責(zé)任鏈裝逼

首先定義一個(gè)抽象類

abstract class AbsLogInterceptChain {
    var next: AbsLogInterceptChain? = null

    open fun intercept(priority: Int, tag: String, logMsg: String?,throwable: Throwable? = null) {
        next?.intercept(priority, tag, logMsg,throwable)
    }
}

然后實(shí)現(xiàn)我們的默認(rèn)打印類

internal class DefaultLoggerInterceptor : AbsLogInterceptChain() {

    override fun intercept(priority: Int, tag: String, logMsg: String?, throwable: Throwable?) {
        next?.intercept(priority, tag, logMsg, throwable)
    }
}

在這基礎(chǔ)上面,對(duì)我們的日志內(nèi)容進(jìn)行美化下,于是添加裝飾類

internal class LoggerDecorateInterceptor : AbsLogInterceptChain() {
    override fun intercept(priority: Int, tag: String, logMsg: String?, throwable: Throwable?) {
        val decorateMsg = "ThreadName: ${Thread.currentThread().name} ---> $logMsg"
        super.intercept(priority, "Umeox_$tag", decorateMsg, throwable)
    }
}

最后實(shí)現(xiàn)真正的打印類

internal class LoggerPrintInterceptor: AbsLogInterceptChain() {
    override fun intercept(priority: Int, tag: String, logMsg: String?,throwable: Throwable?) {
        when(priority) {
            Log.INFO -> {
                Log.i(tag, logMsg?:"-")
            }
            Log.WARN -> {
                Log.w(tag, logMsg?:"-")
            }
            Log.DEBUG -> {
                Log.d(tag, logMsg?:"-")
            }
            Log.ERROR -> {
                Log.e(tag, logMsg?:"-",throwable)
            }
            Log.VERBOSE -> {
                Log.w(tag, logMsg?:"-",throwable)
            }
        }
        super.intercept(priority, tag, logMsg, throwable)
    }
}

在開發(fā)過程中,還有保存日志到本地的需求,如果又添加了保存到本地的鏈接類

internal class Logger2FileInterceptor(private val context: Context): AbsLogInterceptChain() {

    private var file: File? = null
    private val handlerThread = HandlerThread("logger_to_file_thread")

    init {
        handlerThread.start()
    }

    private fun init() {
        Handler(handlerThread.looper).post {
            val format = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA).format(Date())
            context.externalCacheDir?.let {
                file = File(it,"$format.txt")
                if (!file!!.exists()) {
                    try {
                        //在指定的文件夾中創(chuàng)建文件
                        file!!.createNewFile()
                    } catch (e: Exception) {
                        return@post
                    }
                }

                //刪除無用文件 只存放三天的數(shù)據(jù)
                it.listFiles()?.let { files ->
                    for (file in files) {
                        try {
                            val old = SimpleDateFormat(
                                "yyyy-MM-dd",
                                Locale.CHINA
                            ).parse(file.name.replace(".txt", "")) ?: return@let

                            val timeInMillis = Calendar.getInstance().apply {
                                time = old
                            }.timeInMillis

                            val dDay = (System.currentTimeMillis() - timeInMillis) / 1000 / 60 /60 /24
                            if (dDay > 3) {
                                file.delete()
                            }
                        } catch (e:Exception) {
                        }
                    }
                }
            }
        }
    }

    override fun intercept(priority: Int, tag: String, logMsg: String?,throwable: Throwable?) {
        if (!FastLogger.logSave2File && priority != Log.VERBOSE) {
            return
        }
        Handler(handlerThread.looper).post {
            if (file == null) {
                init()
                intercept(priority, tag, logMsg, throwable)
                return@post
            }

            val date = Date()
            val needWriteMessage: String = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(date)
                .toString() + "    " + tag + "    " + logMsg + "\n"
            try {
                file!!.appendText(needWriteMessage)
            } catch (e: Exception) {
                return@post
            }
        }
    }
}

通過責(zé)任鏈的關(guān)系,我們FastLogger最終的實(shí)現(xiàn)就是

object FastLogger {

    private var initialized = false

    private var intercepts:AbsLogInterceptChain = DefaultLoggerInterceptor()

    var logEnable = BuildConfig.DEBUG

    var logSave2File = false

    fun initLogger(context: Context) {
        if (initialized) {
            initialized = true
            return
        }

        val fileInterceptor = Logger2FileInterceptor(context)

        val printInterceptor = LoggerPrintInterceptor()
        printInterceptor.next = fileInterceptor

        val decorateInterceptor = LoggerDecorateInterceptor()
        decorateInterceptor.next = printInterceptor

        intercepts.next = decorateInterceptor
    }

    fun d(tag: String, message: String) {
        log(Log.DEBUG, message, tag)
    }

    fun e(tag: String, message: String, throwable: Throwable? = null) {
        log(Log.ERROR, message, tag, throwable = throwable)
    }

    fun w(tag: String, message: String) {
        log(Log.WARN, message, tag)
    }

    fun i(tag: String, message: String) {
        log(Log.INFO, message, tag)
    }

    fun setInterceptor(interceptor: AbsLogInterceptChain) {
        this.intercepts = interceptor
    }

    @Synchronized
    private fun log(
        priority: Int,
        message: String,
        tag: String,
        throwable: Throwable? = null
    ) {
        if (logEnable) {
            intercepts.intercept(priority, tag, message, throwable)
        }
    }
}

其實(shí),在這個(gè)基礎(chǔ)上面,我們可以抽取出文本保存地址呀,定期清除的時(shí)長(zhǎng)呀等等參數(shù),這個(gè)你們可以下去繼續(xù)封裝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容