自己封装安卓日志打印库

2023-12-14 分析android 系统标准的打印方法 ALOGD ALOGI ALOGE,因为ndk里面是不能使用这些函数,特意花时间跟踪一下这部分代码。本文包含封装log打印函数实例 调用system/core/liblog/include/android/log.h里面的__android_log_print函数。ndk里面是没有libcutils 的。 阅读详情

Logger库说明文档

在日常开发中,日志收集是个非常重要的功能,很多时候,我们都是简单的使用Log.d直接打印我们想要的内容,这样开发当然没问题。包括说简单封装下,统一到一个接口方法打印,也是很常见的手法。我只是在这个基础上面,抽取个简单实现的库而已,没啥特别的。

项目地址

用法

初始化

    private fun initLogger() {
        //设置上下文
        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"))

实现思路

整体实现思路用到了设计模式中的:责任链模式。(假装牛逼)

语言使用的是 Kotlin。

第一步:定义对外接口

首先我们定义我们对外的对象类 FastLogger,并且定义出我们对外的方法

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) {
    }
}

这是四个我们常见的打印方法,我们的所有逻辑都是围绕这四个方法进行开发的。

第二步:实现打印方法

在实现这个方法之前,我们跟一下官方打印的调用流程:

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));
    }

可以看到,最终都是调用的println方法

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

那么最终,其实就是需要以下几个参数:

  • priority:日志级别
  • tag:TAG
  • msg:日志内容

所以,我基于此,定义我们的打印方法,并且通过一个变量控制日志是否可以打印。

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));
        }
    }
}

在这一步,其实,我们一个打印库就封装好了。

只是在这个基础上面,我们实现的和官方提供的没啥区别,就是加了个开关控制而已。我们应该对其美化一下。

第三步:使用责任链装逼

首先定义一个抽象类

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)
    }
}

然后实现我们的默认打印类

internal class DefaultLoggerInterceptor : AbsLogInterceptChain() {

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

在这基础上面,对我们的日志内容进行美化下,于是添加装饰类

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)
    }
}

最后实现真正的打印类

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)
    }
}

在开发过程中,还有保存日志到本地的需求,如果又添加了保存到本地的链接类

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 {
                        //在指定的文件夹中创建文件
                        file!!.createNewFile()
                    } catch (e: Exception) {
                        return@post
                    }
                }

                //删除无用文件 只存放三天的数据
                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
            }
        }
    }
}

通过责任链的关系,我们FastLogger最终的实现就是

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)
        }
    }
}

其实,在这个基础上面,我们可以抽取出文本保存地址呀,定期清除的时长呀等等参数,这个你们可以下去继续封装。

RetrofitUtil(关于retrofit的封装工具类,可添加拦截器) RetrofitUtil(关于retrofit的封装工具类,可添加拦截器) import android.util.Log; import com.example.rrtrofitrxjavamvp_demo.ApiService; import java.io.IOException; import java.util.concurrent.TimeUnit; import okhtt... 阅读详情

相关推荐

Android优秀的日志框架Logger使用详解

简单易用:Logger提供了简洁明了的API,使得日志记录变得简单快捷。多样化的日志等级:Logger支持不同的日志等级,包括VERBOSE、DEBUG、INFO、WARN、ERROR和ASSERT,可以根据需要选择适合的日志等级。丰富的日志输出格式:Logger支持自定义日志输出格式,可以显示时间、线程信息、类名、方法名等。灵活的日志输出方式:Logger支持输出到控制台、文件、服务器等不同的日志输出方式,方便开发者进行日志的查看和分析。性能优越。

. 1689

2 设置loglevel,封装__android_log_print

typedef struct tag_LOG { int logLevel; }LOG, *LPLOG; static LOG gLog = {0}; #define LOGE_LEVEL 1 #define LOGW_LEVEL 2 #define LOGI_LEVEL 3 #define LOGD_LEVEL 4 #define LOGV_LEVEL 5 #ifdef ...

u012906122的专栏 845

移动端跨平台开发(二)一个跨平台的helloworld

在上一篇文章移动端音视频跨平台开发技术概论中,我们分析了跨平台开发的总体架构。今天我们实际动手,写一个helloworld项目,这个项目很简单,就是做一个简单的日志库,最终我们希望能在Android和ios手机上打印出一行hello world日志。 项目名字就叫simplest_crossplatfrom_helloworld,简称sch,对应下文代码中的变量、方法名称。 编写打印日志的核心代码流程 在Android平台上,我们利用jni的log.h进行日志的输出,在ios平台上,则直接利用printf函

张晖的专栏 5859

Android - 你可能需要这样一个日志库

/ 自定义日志格式/** 日志标识 *//** 日志tag *//** 日志内容 *//** 日志等级 *//** 日志生成的时间戳 *//** 日志是否在主线程生成 *//** 日志生成的线程ID */日志存储是通过FLogStore接口实现的,每一个FLogStore对象负责管理一个日志文件。所以需要提供一个工厂为每个日志文件提供FLogStore对象。// 添加日志// 返回当前日志的大小// 关闭如果你看到了这里,觉得文章写得不错就给个赞呗?

sytregn的博客 1339

.NET 4.0下HttpWebRequest请求HTTPS报错?试试这个注册表修改方案

本文深入解析.NET 4.0下HttpWebRequest请求HTTPS报错问题,提供注册表修改方案以解决'基础连接已经关闭'错误。通过调整SchUseStrongCrypto注册表项,强制使用TLS 1.2等现代加密协议,确保安全连接。适用于企业环境部署和长期维护建议。

weixin_29310631的博客 356

KiCad插件安装太慢?试试华秋版,再手把手教你装这5个实用插件

本文介绍了KiCad华秋发行版如何解决插件安装慢的问题,并详细推荐了5个实用插件及其安装方法。华秋版优化了国内用户的下载速度,预装了常用扩展包,包括BOM生成器、3D模型增强工具等,显著提升PCB设计效率。

weixin_29184075的博客 547

Android最好用的日志打印库(自动追踪日志代码位置)

给大家推荐一个自己写的日志打印的库,我愿称之为最强日志打印库:BytUtilLog Byt是Big一统的缩写,大一统日志打印库,哈哈!关于它的功能:它可以控制整个日志是否输出,打印的日志可以直接跳转到代码的位置,方便查看调试,同时解决输入内容一样的时候不知道是哪一处的日志的问题,自动追踪日志代码位置。也看过前辈们写的日志打印库,我对自己的库在多线程的情景下的表现,还是有担心的,如果有遇到问题,可以github或者csdn 本篇上留言都可以,看到会及时回复处理的。

门徒07的博客 587

从报错到优化:一次Gauss JDBC参数限制排查实录(32767参数上限避坑指南)

本文详细记录了排查GaussDB JDBC驱动因绑定参数数量超过32767上限而抛出`PSQLException`(报错信息为`Tried to send an out-of-range integer as a 2-byte value`)的全过程。文章深入分析了该限制的底层协议原理,并提供了应用层分批查询、使用临时表以及优化业务逻辑等多种解决方案,旨在帮助开发者规避此参数上限陷阱,构建更稳健的数据访问层。

rgv23456789的博客 218

NDK 封装日志库 __android_log_print 输出 写到文件中记录

文章目录前言定义方法和变量编写代码开发时的重新封装 前言 开发移动项目中,有C++提供的so,调用so中函数时会输出打印,so中采用的是 __android_log_print 输出打印信息,因为要记录so中的问题,比如数据的采集正确性,偶尔崩溃的原因时需要通过日志信息来分析,所以就有了这个打算,将此输出写入到文件中去,当然写到文件中去后,也可以在控制台看到,当然发布时也可以不在控制台看到,所以下面列出几点需求 初步需求如下 供c++代码调用 控制台输出 文件输出(可控制文件大小) 可设置日志等级 定义

波风水门 8237

CATIA高效设计:自定义快捷键全攻略

本文详细介绍了如何在CATIA中自定义快捷键以提升设计效率。通过减少鼠标依赖、优化工作流程和减轻手腕疲劳,合理设置快捷键可显著提高建模速度。文章提供了从基础设置到高级配置的全流程指南,包括解决冲突、推荐方案及外设扩展建议,帮助用户快速掌握CATIA高效设计技巧。

weixin_42648844的博客 584

微信小程序PC端兼容性实战:如何绕过限制在电脑上运行手机端小程序

本文详细解析了微信小程序在PC端的兼容性问题及解决方案,重点介绍了如何利用`wx.getSystemInfo`API检测运行环境,并通过修改User-Agent、动态环境检测与适配等技术手段实现手机端小程序在PC端的顺畅运行。文章还提供了常见兼容性问题的解决方法和调试工具推荐,帮助开发者高效应对多端适配挑战。

weixin_28366053的博客 865

Flutter _ 日志还能这么打印,太秀了!,校园安卓面试题

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。还有。

2401_84149836的博客 1054

Matlab实战:用递归最小二乘法(RLS)搞定动态系统参数辨识(附完整代码)

本文通过Matlab实战,详细介绍了如何利用递归最小二乘法(RLS)进行动态系统参数辨识。文章从理论优势切入,提供了完整的仿真数据生成、RLS算法核心实现代码,并对参数收敛过程、预测误差及不同遗忘因子的影响进行了可视化分析与性能评估,为工程实践提供了清晰的指南。

weixin_29035405的博客 503

探索高效日志管理:Android-PLog 开源库推荐

探索高效日志管理:Android-PLog 开源库推荐 在Android应用开发过程中,日志管理是不可或缺的一部分。一个优秀的日志工具不仅能帮助开发者快速定位问题,还能提高开发效率。今天,我们将介绍一款名为 Android-PLog 的开源日志封装库,它以其纯粹、美观和强大的特性,成为了Android开发者日志管理的得力助手。 项目介绍 Android-PLog(简称 PLog)是一个专为Andr...

gitblog_00021的博客 399

14、多层螺旋 CT 技术详解

本文详细解析了多层螺旋CT(MSCT)技术的原理与应用,涵盖其相对于单层螺旋CT的优势,包括图像采集速度提升、分辨率提高和噪声降低。文章深入探讨了扫描仪的几何结构、离散投影模型以及关键参数如切片宽度和螺距的影响,并重点介绍了z-滤波重建算法及其离散实现流程。通过流程图和公式推导,系统梳理了从投影获取到图像重建的全过程。同时分析了MSCT在临床中的实际应用案例及未来发展趋势,如更高分辨率、更低辐射剂量和智能化算法等方向,全面展示了MSCT在现代医学影像中的核心价值。

f0g1h2的博客 193

车企自动泊车技术全解|纯视觉/多传感器融合双路线、端到端大模型革新 助力机械车位/无划线车位/极窄车位精准泊车落地

随着智能驾驶技术全面下沉,自动泊车已从高端车型专属配置,普及至十万级家用车型,成为车载核心刚需功能。但市面主流车企的泊车能力差距极大,核心差异源于传感器硬件架构、算法迭代逻辑、场景训练体系的全方位分化。传统规则式泊车算法仅适配标准划线车位,面对地库暗光、标线磨损、机械双层车位、极窄靠墙车位、无划线空地泊车等真实长尾场景极易失效。

getusushu的博客 105

Android JNI高效日志打印:C++层封装与实战技巧

本文详细介绍了Android JNI开发中C++层日志打印的高效封装与实战技巧。通过头文件封装、CMake配置优化及高级日志技巧(如条件日志、异步日志等),帮助开发者解决JNI调试难题,提升Native代码的日志管理效率和性能。特别适合需要优化Android Native开发的工程师参考。

weixin_29189987的博客 368

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Flutter _ 日志还能这么打印,太秀了!(1),成功定级腾讯T3-2

Android架构学习进阶是一条漫长而艰苦的道路,不能靠一时激情,更不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!上面分享的字节跳动公司2020年的面试真题解析大全,笔者还把一线互联网企业主流面试技术要点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。就先写到这,码字不易,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我。

2401_84149824的博客 1183

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

retrofit接口日志拦截器

我们在使用retrofit调用后台接口的时候,往往由于封装的比较严密,我们在查看自己请求的参数和获取返回的原始数据的时候比较麻烦,那么我们都会希望能够在封装的请求基类里面进行统一的日志打印,这时候就需要构建一个拦截器。 Android studio的引入: compile 'com.github.zhaokaiqiang.klog:library:1.6.0' compile 'co

张迅之的博客 813

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Android OkHttpClient+应用拦截器+日志拦截器

  /**  * Created by mumu on 2018/6/14.  */   public class OkhtttpUtils {     private static OkhtttpUtils mOkhtttpUtils;     private OkHttpClient mOkHttpClien;     private final Handler mHandler;  ...

as89751的博客 546

使用android日志工具

Log的级别? 日志级别按照高低排序为:ERROR,WARN,INFO,DEBUG,VERBOSE, 日志输出: Log.e()输出ERROR级别的日志信息 Log.w()输出WARN,ERROR级别的日志 Log.i()输出WARN,ERROR级别的日志 Log.d() 输出DEBUG,INFO,WARN,ERROR级别的日志 Log.v () 输出VERBOSE,DEB...

weixin_30835649的博客 123

Android-PLog:一款优秀的Android日志库

Android-PLog:一款优秀的Android日志库 Android-PLog 是一个专为 Android 应用程序设计的开源日志封装库,以追求纯粹、易用、强大为核心理念。该项目的编程语言主要为 Java。 核心功能 Android-PLog 的核心功能主要包括: 容易打印:支持无 Tag 打印,支持空消息打印(通常用于观察某处是否执行),支持任意参数类型,支持变长参数和自动格式化,支持 B...

gitblog_00056的博客 477

[Android开源] EasyAndroid:专注于提供各种基础组件封装的集成组件库

在平时的开发过程中,我们经常会需要使用到一些基础功能组件,比如Toast,比如Log等。 而这些功能组件,在开发时需要使用到的功能点其实相当有限,所以这也意味着,我们对此类组件的要求是:简单、轻量、易用!相对应的,此类组件的封装库,也应该尽量实现得轻巧精练 EasyAndroid即是专门针对此种需求所设计的一款基础组件集成库: 宗旨 1. 设计独立 组件间独立存在,不相互依赖,若只需要集成库中的部分功能,可直接非常方便的copy源码进行使用。 2. 设计轻巧 因为是组件集成库,所以要求每个组件的设计尽

Android061503的博客 537

开源库MusicPlayManager - 封装StarrySky音乐库

这是我最近重构简易音乐的时候看了一下StarrySky的使用,19年还是00年那会就想着要基于StarrySky做一个单例一样的管理类,后面不了了之了,现在因为重构项目,想着可以基于StarrySky来做一个用户几乎不需要改动的封装,来实现自定义音乐通知栏、只需要输入一个target页面来接收页面跳转即可,用户可以在使用之前再进行初始化。MusicPlayManager提供音乐播放封装、自定义通知栏及圆角、日志功能、权限申请、无入侵获取上下文以及防恶意点击等。项目的build里添加如下。......

Tobey_r1的博客 1799

【Abyss】Android平台BPF和SECCOMP的SVC指令拦截

Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。

chendianbo的博客 1455

android中有哪些utils的作用,GitHub - QQ986945193/DAndroidUtils: gradle引用android开发中常用功能封装的library...

DAndroidUtilsDAndroidUtils是(程序员小冰)david在Android开发中的一些常用功能封装。其中功能包括多种,如:多种实现loading等待框。你只需引入library之后一行代码实现此功能。引入依赖非常简单支持导入library,gradle和maven等多种方式。其中常用工具类utils包括:1,log打印日志的工具类2,Toast封装工具类3,Activity的管...

weixin_31162865的博客 275
下一篇: DataStore-SharedPreference知识点
fastcv
博客等级 码龄10年 90粉丝 5原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值