一个Android的Log类

优点:

  • 代码级禁用,DEBUG才有日志可以看到
  • isLoggable运行时拦截
  • 零泄露风险
  • 边框包裹,自动格式化
  • 精准定位,点击跳转
  • 使用简单,动态调用和TAG
  • 零开销潜力,内存友好

下面是类的内容:

import android.util.Log
import com.orhanobut.logger.Logger
import com.orhanobut.logger.PrettyFormatStrategy
import your.package.app.BuildConfig

/**
 * 日志工具类
 * 核心规则:Release包完全禁用所有日志输出,Debug包正常输出格式化日志
 * 支持日志级别:VERBOSE/DEBUG/INFO/WARN/ERROR
 * 支持场景:无Tag日志、自定义Tag、带异常堆栈、可变参数格式化
 */
object LogUtils {
    // 全局默认Tag(可根据项目修改)
    private const val DEFAULT_TAG = "MY_APP"

    /**
     * 初始化日志配置(在App.kt的onCreate中调用)
     */
    fun init() {
        if (BuildConfig.DEBUG) {
            // Debug模式:初始化格式化日志配置
            val formatStrategy = PrettyFormatStrategy.newBuilder()
                .showThreadInfo(false)      // 显示线程信息
                .methodCount(2)            // 显示调用方法行数(2行足够定位)
                .methodOffset(1)           // 方法偏移量
                .tag(DEFAULT_TAG)          // 默认Tag
                .build()
            Logger.addLogAdapter(object : com.orhanobut.logger.AndroidLogAdapter(formatStrategy) {
                // 仅Debug模式允许日志输出
                override fun isLoggable(priority: Int, tag: String?): Boolean = BuildConfig.DEBUG
            })
        } else {
            // Release模式:移除所有日志适配器,确保无日志输出
            Logger.clearLogAdapters()
        }
    }

    // ====================== 基础日志方法(无Tag,使用默认Tag) ======================
    fun v(message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.v(message, *args)
    }

    fun d(message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.d(message, *args)
    }

    fun i(message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.i(message, *args)
    }

    fun w(message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.w(message, *args)
    }

    fun e(message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.e(message, *args)
    }

    // ====================== 带异常堆栈的日志(无Tag) ======================
    fun e(throwable: Throwable, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.e(throwable, message, *args)
    }

    // ====================== 自定义Tag的日志方法 ======================
    fun v(tag: String, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).v(message, *args)
    }

    fun d(tag: String, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).d(message, *args)
    }

    fun i(tag: String, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).i(message, *args)
    }

    fun w(tag: String, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).w(message, *args)
    }

    fun e(tag: String, throwable: Throwable, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).e(throwable, message, *args)
    }

    fun e(tag: String, message: String, vararg args: Any) {
        if (BuildConfig.DEBUG) Logger.t(tag).e(message, *args)
    }

    // ====================== 兼容原生Log的极简方法(可选) ======================
    /**
     * 极简Debug日志(自动取调用类名作为Tag)
     */
    fun simpleD(message: String) {
        if (BuildConfig.DEBUG) {
            val tag = getCallerClassName()
            Log.d(tag, message)
        }
    }

    /**
     * 极简Error日志(自动取调用类名作为Tag,带异常)
     */
    fun simpleE(throwable: Throwable, message: String) {
        if (BuildConfig.DEBUG) {
            val tag = getCallerClassName()
            Log.e(tag, message, throwable)
        }
    }

    /**
     * 私有方法:获取调用者的类名(用于simple系列日志的自动Tag)
     */
    private fun getCallerClassName(): String {
        return try {
            // 获取调用栈,定位到LogUtils外部的调用类
            val stackTrace = Thread.currentThread().stackTrace
            val callerStackIndex = 4 // 栈索引:0=VM,1=Thread,2=getCallerClassName,3=simpleD/E,4=外部调用者
            val callerClass = Class.forName(stackTrace[callerStackIndex].className)
            callerClass.simpleName // 只取类名(不含包名),简化Tag
        } catch (e: Exception) {
            DEFAULT_TAG // 异常时使用默认Tag
        }
    }

    /**
     * 清空日志(Release模式下无操作)
     */
    fun clear() {
        if (BuildConfig.DEBUG) Logger.clearLogAdapters()
    }
}

在使用之前,需要在Application 中调用:

LogUtils.init()
//记得在Manifest文件中检查是否为自定义的Application

使用示例

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import your.package.app.common.LogUtils // 导入你的日志工具类

class MainActivity : AppCompatActivity() {
    private val USER_ID = 1001

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 使用封装后的日志方法,它会在 Debug 包中格式化输出
        LogUtils.d("MainActivity 已启动") // 无Tag,使用默认Tag
        
        // 使用带参数的格式化日志
        LogUtils.i("用户 %d 正在加载数据...", USER_ID) 
        
        // 使用自定义 Tag
        LogUtils.w("UI", "Warning: 布局文件加载可能耗时") 
        
        // 模拟一个异常日志
        try {
            // 模拟抛出异常
            throw RuntimeException("网络连接失败")
        } catch (e: Exception) {
            // 输出带异常堆栈的日志
            LogUtils.e(e, "数据加载失败,请检查网络!") 
        }
    }
}

最终可能的输出效果:


// LogUtils.d("MainActivity 已启动") 的输出
D/MY_APP: ┌────────────────────────────────────────────────────────
D/MY_APP: │ MainActivity.onCreate (MainActivity.kt:15)
D/MY_APP: ├────────────────────────────────────────────────────────
D/MY_APP: │ MainActivity 已启动
D/MY_APP: └────────────────────────────────────────────────────────

// LogUtils.e(e, "数据加载失败,请检查网络!") 的输出
E/MY_APP: ┌────────────────────────────────────────────────────────
E/MY_APP: │ MainActivity.onCreate (MainActivity.kt:28)
E/MY_APP: ├────────────────────────────────────────────────────────
E/MY_APP: │ RuntimeException: 网络连接失败
E/MY_APP: │   at your.package.app.MainActivity.onCreate(MainActivity.kt:24)
E/MY_APP: │   ... (省略部分堆栈)
E/MY_APP: ├────────────────────────────────────────────────────────
E/MY_APP: │ 数据加载失败,请检查网络!
E/MY_APP: └────────────────────────────────────────────────────────

图片来自: https://www.pexels.com/zh-cn/photo/1260324

Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注