美文网首页Android开发Run_实用Android代码封装
史上最强的 Android 日志库 XLog

史上最强的 Android 日志库 XLog

作者: ElvisHew | 来源:发表于2016-11-13 00:39 被阅读2135次

    Github: https://github.com/elvishew/xLog

    简单、美观、强大、可扩展的 Android 和 Java 日志库,可同时在多个通道打印日志,如 Logcat、System.out 和文件。如果你愿意,甚至可以打印到远程服务器(或其他任何地方)。

    XLog 能干什么:

    • 全局配置(TAG,各种格式化器...)或基于单条日志的配置
    • 支持数组
    • 支持无限长的日志(没有 4K 字符的限制)
    • XML 和 JSON 格式化输出
    • 线程信息(线程名等,可自定义)
    • 调用栈信息(可配置的调用栈深度,调用栈信息包括类名、方法名文件名和行号)
    • 保存日志文件(文件名和自动备份策略可灵活配置)
    • 在 Android Studio 中的日志样式美观
    • 简单易用,扩展性高

    与其他日志库的不同:

    • 优美的源代码,良好的文档
    • 扩展性高,可轻松扩展和强化功能

    依赖

    compile 'com.elvishew:xlog:1.1.0'
    

    预览

    • 带线程信息、调用栈信息和边框的日志


      classic_log.png
    • 日志文件


      log_files.png

    架构

    architecture.png

    用法

    初始化

    简单方式

    XLog.init(LogLevel.ALL);
    

    或者如果你想要在正式版中禁止打日志

    XLog.init(BuildConfig.DEBUG ? LogLevel.ALL : LogLevel.NONE);
    

    高级方式

    LogConfiguration config = new LogConfiguration.Builder()
        .tag("MY_TAG")                                         // 指定 TAG,默认为 "X-LOG"
        .t()                                                   // 允许打印线程信息,默认禁止
        .st(2)                                                 // 允许打印深度为2的调用栈信息,默认禁止
        .b()                                                   // 允许打印日志边框,默认禁止
        .jsonFormatter(new MyJsonFormatter())                  // 指定 JSON 格式化器,默认为 DefaultJsonFormatter
        .xmlFormatter(new MyXmlFormatter())                    // 指定 XML 格式化器,默认为 DefaultXmlFormatter
        .throwableFormatter(new MyThrowableFormatter())        // 指定可抛出异常格式化器,默认为 DefaultThrowableFormatter
        .threadFormatter(new MyThreadFormatter())              // 指定线程信息格式化器,默认为 DefaultThreadFormatter
        .stackTraceFormatter(new MyStackTraceFormatter())      // 指定调用栈信息格式化器,默认为 DefaultStackTraceFormatter
        .borderFormatter(new MyBoardFormatter())               // 指定边框格式化器,默认为 DefaultBorderFormatter
        .addObjectFormatter(AnyClass.class,                    // 为指定类添加格式化器
                new AnyClassObjectFormatter())                 // 默认使用 Object.toString()
        .build();
    
    Printer androidPrinter = new AndroidPrinter();             // 通过 android.util.Log 打印日志的打印器
    Printer SystemPrinter = new SystemPrinter();               // 通过 System.out.println 打印日志的打印器
    Printer filePrinter = new FilePrinter                      // 打印日志到文件的打印器
        .Builder("/sdcard/xlog/")                              // 指定保存日志文件的路径
        .fileNameGenerator(new DateFileNameGenerator())        // 指定日志文件名生成器,默认为 ChangelessFileNameGenerator("log")
        .backupStrategy(new MyBackupStrategy())                // 指定日志文件备份策略,默认为 FileSizeBackupStrategy(1024 * 1024)
        .logFlattener(new MyLogFlattener())                    // 指定日志平铺器,默认为 DefaultLogFlattener
        .build();
    
    XLog.init(LogLevel.ALL,                                    // 指定日志级别,低于该级别的日志将不会被打印
        config,                                                // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build()
        androidPrinter,                                        // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter
        systemPrinter,
        filePrinter);
    

    对于 android,做初始化的最佳地方是 Application.onCreate()

    全局用法

    XLog.d("Simple message")
    XLog.d("My name is %s", "Elvis");
    XLog.d("An exception caught", exception);
    XLog.d(object);
    XLog.d(array);
    XLog.json(unformattedJsonString);
    XLog.xml(unformattedXmlString);
    ... // 其他全局使用
    

    局部用法

    创建一个 Logger

    Logger partial = XLog.tag("PARTIAL-LOG")
        ... // 其他配置
        .build();
    

    然后对该 Logger 进行局部范围的使用,所有打印日志的相关方法都跟 XLog 类里的一模一样。

    partial.d("Simple message 1");
    partial.d("Simple message 2");
    ... // 其他局部使用
    

    基于单条日志的用法

    进行基于单条日志的配置,然后就可以直接打印日志了,所有打印日志的相关方法都跟 XLog 类里的一模一样。

    XLog.t()    // 允许打印线程信息
        .st(3)  // 允许打印深度为3的调用栈信息
        .b()    // 允许打印日志边框
        ...     // 其他配置
        .d("Simple message 1");
    
    XLog.tag("TEMP-TAG")
        .st(0)  // 允许打印不限深度的调用栈信息
        ...     // 其他配置
        .d("Simple message 2");
    
    XLog.nt()   // 禁止打印线程信息
        .nst()  // 禁止打印调用栈信息
        .d("Simple message 3");
    
    XLog.b().d("Simple message 4");
    

    比较

    让我们设想有一个 JSON 字符串和一个 XML 字符串:

    String jsonString = "{\"name\": \"Elvis\", \"age\": 18}";
    String xmlString = "<team><member name="Elvis"/><member name="Leon"/></team>";
    

    Android Log

    Log.d(TAG, "Message");
    Log.d(TAG, String.format("Message with argument: age=%s", 18));
    Log.d(TAG, jsonString);
    Log.d(TAG, xmlString);
    Log.d(TAG, "Message with stack trace info", new Throwable());
    
    comparison-android-log.png

    XLog

    XLog.init(LogLevel.ALL);
    XLog.d("Message");
    XLog.d("Message with argument: age=%s", 18);
    XLog.json(jsonString);
    XLog.xml(xmlString);
    XLog.st(5).d("Message with stack trace info");
    
    comparison-xlog.png

    带边框的 XLog

    XLog.init(LogLevel.ALL, new LogConfiguration.Builder().b().build());
    XLog.d("Message");
    XLog.d("Message with argument: age=%s", 18);
    XLog.json(jsonString);
    XLog.xml(xmlString);
    XLog.st(5).d("Message with stack trace info");
    
    comparison-xlog-with-border.png

    兼容性

    为了兼容 Android Log,XLog 支持 Android Log 里的所有方法。
    请看 XLog 里的 Log 类。

    Log.v(String, String);
    Log.v(String, String, Throwable);
    Log.d(String, String);
    Log.d(String, String, Throwable);
    Log.i(String, String);
    Log.i(String, String, Throwable);
    Log.w(String, String);
    Log.w(String, String, Throwable);
    Log.wtf(String, String);
    Log.wtf(String, String, Throwable);
    Log.e(String, String);
    Log.e(String, String, Throwable);
    Log.println(int, String, String);
    Log.isLoggable(String, int);
    Log.getStackTraceString(Throwable);
    

    Github: https://github.com/elvishew/xLog

    关于作者
    ElvisHew 的 Github 主页: https://github.com/elvishew
    ElvisHew 的新浪微博:http://weibo.com/elvishew

    相关文章

      网友评论

      • 未见哥哥:在 app release 环境下,如果使用将log写入到文件中的化,会不会有性能问题?是否建议将其作为线上日志收集器?
        ElvisHew:@六号表哥 你可以实测一下。我做过压力测试,没问题的
        未见哥哥:@ElvisHew 我们需要在release版本,提取业务日志,不知道性能如何
        ElvisHew:@六号表哥 一般的应用应该不会的,写文件是异步线程在写。另外,实在担心的话,你可以在release版就不写文件,或许过滤掉低级别的日志
      • 412c05046578:项目中实测,会出现丢失日志的情况
        412c05046578:@ElvisHew 经过半个月线上实践,问题已经有效解决,谢谢反馈!
        提一个小意见,建议添加日志最大保存时长的接口,比如最多保存7天的日志,期待新版本!
        412c05046578:@ElvisHew 好的,我试一下
        ElvisHew:默认使用的是 FileSizeBackupStrategy(1024 * 1024) 备份策略,即单个日志文件最多只能 1M,大了会被重命名为 .bak 文件。
        你可以将这个指定得大一些,比如 10M。或者直接指定 NeverBackupStrategy,即不限制单个日志文件的大小。
      • 有妖气_20e6:你好,是这样的,我以前项目中有个module是library的(其中使用到了Xlog库,我的引用方式是:compile 'com.elvishew:xlog:1.4.0添加依赖),现在想把这个module移植到别的项目中去。所以我提取了这个module的aar包。但是在其他项目运行报错找不到Xlog这个类。根据网上说的,是因为新项目中没有依赖Xlog的原因,但是我在新项目中添加依赖Xlog库还是报错。
        ElvisHew:@有妖气_20e6 这个严格来说跟 xlog 无关了。不过,你报的是什么错?理论上你在新项目中可以依赖 library.aar 及 compile xlog 就可以了的。
      • 那一年我两岁:你好,我这边实现了将Log日志保存在手机SD卡中,但是打开的时候是只读模式而且会出现部分乱码,也无法通过蓝牙的方式发送出去
        ElvisHew:你的应该是编码问题了。写是UTF8,看你怎么读
      • 金鲨:你好好喜欢你这个框架,测试了一下确实很不错,但是有个问题望请大神解决一下,
        就是我想用当前调用类名作为tag标签进行设置,方便查找及调试查看,但是我只发现全局设置和局部设置,需要怎么设置一下?能想全局一样设置搞定吗?
        ElvisHew:@金鲨 目前不支持这个功能。我能想到的办法是,依然是使用局部设置,并设法让这个局部设置更简单一些。比如不同类名对应不同的局部 Logger,相同的类名使用相同的 Logger。
      • 一剑飞鸿:请问,怎样把写到本地后缀为.log日志文件后缀改成.txt的呢?
        ElvisHew:@一剑飞鸿 默认也不是.log文件,应该是文件名字就叫log。你只要指定一下FileNameGenerator,文字名字可以随便取的。
      • 海南鸡:请问如何实现日志定期清理呢?比如说日志文件只保留10天的。还有日志内容如何实现加密?
        ElvisHew:@海南鸡 分三点来回答你。
        1. 如何实现日志定期清理
        目前无法实现定期清理。
        2. 如何让日志文件只保留10天
        换个提法吧,如何让日志文件以10天为期自动备份。可以设置一个 BackupStrategy,超过指定时间则让它自动备份。
        3. 如何实现加密
        分两小点。
        3-a. 如何实现全局加密,即所有 Printer 都加密
        用 Interceptor 实现,对 log 参数进行加密。
        3-b. 如何实现日志文件的加密
        用 Flattener 实现。可实现自己的 Flattener,或覆写已有 Flattener 如 ClassicFlattener 的 flatten 方法。若只加密日志文本,只需处理 log 参数;若需加密整个日志记录,则处理 flatten 后的文本。

        希望可以帮到你
      • LeeBraveheart:请问能不能按照日志级别去写文件,比如我打印了e,i,d,w,v每个级别的日志,但是只希望e的日志写入文件,求解答?
        LeeBraveheart: @LeeBraveheart 谢谢
        ElvisHew:可以的,请使用 LogInterceptor,把 e 级别的日志拦截掉
      • 西马塔:您好,保存到本地的日志文件的结构可以自定义吗?可以在Xlog文件夹内,分日期文件夹,再在日期文件夹内以loglevel的形式保存日志文件吗?
        ElvisHew:@西马塔 你好,目前不支持这种分文件夹的形式。我仔细想了想,这种需求太小众了,也不好搞,所以短期内不会支持,不好意思了。
      • hucw:你好,在5.0以上需要动态申请写入SD卡权限,如果在Application里面初始化XLog如何实现权限申请?
        QuincySx: @ElvisHew 可以写到 SD/Android/data 文件夹下、不需要权限,卸载App还能自动删除
        ElvisHew:@初学者_Huy 是6.0以上吧?如果只在调试阶段需要写日志到SD卡,你可以简单地手动去设置里打开。如果在正式版本里也想写日志到SD卡,我建议在Application里不要初化化FilePrinter,而是在拿到SD卡权限后(如某Activity)里,再调用 XLog.printers(printers).build() 重新生成一个带 FilePrinter 的Logger,后续就使用这个Logger。
        hucw:不好意思,是我在activity基类里未授权前打印了Log,所以出现无权限操作日志文件错误。
      • 避雨客:你好,请问下,这个XLog和微信mars组件里面的Xlog有什么区别?是同一个吗?
        避雨客:@ElvisHew 嗯嗯,昨天有看,是有很大的区别,感觉还是你的简单一些:smile:
        ElvisHew:@避雨客 腾讯最近开源了 mars,你可以看一下。
        ElvisHew:@避雨客 不是,也很不一样
      • 渐渐懂了吧:请问打印到服务器怎么实现呢?不是很明白逻辑
        ElvisHew:@渐渐懂了吧 实际上只是描述一种可能性。比如实现自己的 Printer,在 println 里调用 REST API 把 LOG 上传到服务器上。上传策略也是自己实现。
      • Jlanglang:标题让我想起了各直播上的各种国服第一
        ElvisHew:@Jlanglang 嗯,标题党了。欢迎批评指正提建议
      • 黑boy:不知道为啥 我的日志文件中中文乱码了 :disappointed_relieved:
        黑boy:@黑boy 知道了
        ElvisHew:@黑boy 应该是写日志用的编码和你查看时用的编码不一致。试试切换一下编码再查看
      • 希腊明天:还有我发现,文件最多储存1M日志内容,这个大小可以自己配置吗?
        ElvisHew:@希腊明天 嗯。现在功能还在不断开发中,文档肯定要加上,需要些时间。
        希腊明天:@ElvisHew 好的,谢谢您的回答!不过像这样的用法,建议您写到git文档上。
        ElvisHew:@希腊明天 你可以
        Printer filePrinter = new FilePrinter
        .Builder(new File(dir).getPath())
        .backupStrategy(new FileSizeBackupStrategy(maxFileSize))
        ...
        .build();

        到达这个指定大小之后,会将当前的 xxx 文件重命名为 xxx.bak 文件,并重新在一个全新的 xxx 文件中继续打针日志。

        当然,如果你不想自动备份,你可以自定义一个永不备份的 BackupStrategy。

      • 希腊明天:你好,我们的一个项目,在用你的框架。但我发现日志打印到文件,文件中的时间,怎么是一个时间戳呢?为什么不转成2016-xx-xx xx:xx:xx 这种格式的呢。
        希腊明天:@ElvisHew 昂,,,这样啊,谢谢提醒。
        ElvisHew:@希腊明天 你可以自定义你自己的 LogFlattener。
        ```
        Printer filePrinter = new FilePrinter
        .Builder(new File(dir).getPath())
        .logFlattener(new MyLogFlattener())
        ...
        .build();

        private class MyLogFlattener implements LogFlattener {

        private ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>() {

        @Override
        protected SimpleDateFormat initialValue() {
        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US);
        }
        };

        @Override
        public CharSequence flatten(int logLevel, String tag, String message) {
        return formatter.get().format(new Date())
        + '|' + LogLevel.getShortLevelName(logLevel)
        + '|' + tag
        + '|' + message;
        }
        }
        ```
      • 范蓄能:厉害了我的Elvis
      • 灰6太9狼:开眼了……

      本文标题:史上最强的 Android 日志库 XLog

      本文链接:https://www.haomeiwen.com/subject/aqtcpttx.html