前言
几种常用的日志库
通过对比:logger、timber、xLog、KLog、log4j 日志库 ;个人最终选择了logger;当然另外几个也很强大;只是 从个人的开发需求角度来说 logger更适合本人罢了(早期个人使用过log4j日志库)。
给出上述库的飞升台:
logger:
https://github.com/orhanobut/logger
timber:
https://github.com/JakeWharton/timber
xLog:
https://github.com/elvishew/xLog
KLog:
https://github.com/ZhaoKaiQiang/KLog
为啥弃用API中的Log
在解析这个log库前,我们首先要明白 我们为啥要用三方的单独的库来操作程序,毕竟原生的log的功能已经基本能满足我们的开发需求了;为啥还要添加这个库 呢? why? 这不是增加了apk大小的负担吗 ?
是的 ;没错原生的log确实已经很好了;but 他太基础了;
原因是我们在使用的原生的log的时候会发现:
1:很多TAG都要写下雷同的代码;
2:在打印数据比较长(比如一个很长的json)他是有限制的:打印不全!
3:输出控制台输出的日志太多;很难找到在哪里。
4:手机(统称虚拟机)断开调试 ;日志就回老家了。
5:测试的bug日志能帮助我们快速定位到问题的所在。
6:总之:使用原生的日志信息不利于我们更好的装逼。
理想中的Log日志
动手分析之前,我们要先弄清楚,我们需要什么样的log:
打印日志是一门传统艺术,历史悠久,打从有编程那年就开始有了。它与单步调试并称程序调试两大神技,程序员们因为拥护不同的调试方法,分裂成了两大阵营:日志派和调试派,两派互相看不起。调试派说打印日志太没技术含量,还要到处做标记,看我们调试派,想停哪里点哪里,想看什么看什么,调试器能带我飞。日志派也不服气,你能离线调试吗?你能调试多线程错误吗?你能xxx调试吗?( oreofish分析的透彻啊(∩_∩) )
好程序员两种技能都要掌握,现在调试工具越来越好用,单步调试没有任何困难。但是打印日志仍然是不可或缺的必杀技,尤其是查活系统(运行中软件)的问题时,如果没有后台日志或者用户手机日志,真是打死也不知道哪里出了问题。
扯完日志的重要性,认真说说我们对日志系统的需求:
- 容易打印:无论什么格式的数据,上到自定义类,下到字节流,都能单行代码给打出来,总之打印一切object,360度无死角。
- 格式工整:对显示的日志排版简洁工整。
- 代码简洁:就是要求我们写的代码少而清晰。
- 日志定位:能够快速对存在问题的逻辑进行定位;也就是能link.。
- 设置简单:我们的日志框架要容易上手;设置简单。
- 保存日志:尽量能在没有环境的真机上运行的时候保存日志信息,便于需要时查看。
- 上线0负担:不能因为log降低我们的 release包效率。
如果能做到上面几点,那么这个日志系统基本上就很好了。那么标尺有了,基本上我们要使用哪个log也就清楚了(至少笔者使用的清楚了)。
下面我们挑出来一种和Logger库对比下:
xLog使用解析
为啥要首先说xLog这个库呢? 因为 本人本打算用这个库来开发来自;毕竟它小且强大;最重要的是它能保存文件;理论上是本人的首选;
but…试了一个多小时 楞是没有保存文件到本地成功;不知道啥情况;果断弃之;于是符 ;我决定改用Logger库;在github那是这几个日志库中start最高的了吧。 很棒。
咱要站在巨人的肩膀 前进……娃哈哈哈O(∩_∩)O~
额 跑题了——开搞 开搞——
github 这样来描述xLog的使用:
XLog概要:
简单、美观、强大、可扩展的 Android 和 Java 日志库,可同时在多个通道打印日志,如 Logcat、Console 和文件。如果你愿意,甚至可以打印到远程服务器(或其他任何地方)。
总之: 很强大、非常强大
XLog 能干什么:
- 全局配置(TAG,各种格式化器…)或基于单条日志的配置
- 支持打印任意对象以及可自定义的对象格式化器
- 支持打印数组
- 支持打印无限长的日志(没有 4K 字符的限制)
- XML 和 JSON 格式化输出
- 线程信息(线程名等,可自定义)
- 调用栈信息(可配置的调用栈深度,调用栈信息包括类名、方法名文件名和行号)
- 支持日志拦截器
- 保存日志文件(文件名和自动备份策略可灵活配置)
- 在 Android Studio 中的日志样式美观
- 简单易用,扩展性高
总之: 它能干你想要的一切。
与其他日志库的不同:
优美的源代码,良好的文档
扩展性高,可轻松扩展和强化功能
轻量级,零依赖
依赖:
compile 'com.elvishew:xlog:1.3.0'
预览:
1:首页看一个个人真机上运行的显示日志的效果:
2:带线程信息、调用栈信息和边框的日志
3:格式化后的网络 API 请求
4:格式化后的网络 API 响应
5:日志文件
架构:
上面的部分就是直观的告诉你:xLog很强大,他要说的就是这句话;不过也确实强大。
使用方式:
1:简单方式:在程序入口添加
XLog.init(LogLevel.ALL);
或者:
//或者如果你想要在正式版中禁止打日志:很有必要 ;可以优化release版本。
XLog.init(BuildConfig.DEBUG ? LogLevel.ALL : LogLevel.NONE);
2:高级方式:
LogConfiguration config = new LogConfiguration.Builder()
.logLevel(BuildConfig.DEBUG ? LogLevel.ALL // 指定日志级别,低于该级别的日志将不会被打印,默认为 LogLevel.ALL
: LogLevel.NONE)
.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()
.addInterceptor(new BlacklistTagsFilterInterceptor( // 添加黑名单 TAG 过滤器
"blacklist1", "blacklist2", "blacklist3"))
.addInterceptor(new MyInterceptor()) // 添加一个日志拦截器
.build();
Printer androidPrinter = new AndroidPrinter(); // 通过 android.util.Log 打印日志的打印器
Printer consolePrinter = new ConsolePrinter(); // 通过 System.out 打印日志到控制台的打印器
Printer filePrinter = new FilePrinter // 打印日志到文件的打印器
.Builder("/sdcard/xlog/") // 指定保存日志文件的路径
.fileNameGenerator(new DateFileNameGenerator()) // 指定日志文件名生成器,默认为 ChangelessFileNameGenerator("log")
.backupStrategy(new NeverBackupStrategy() // 指定日志文件备份策略,默认为 FileSizeBackupStrategy(1024 * 1024)
.logFlattener(new MyFlattener()) // 指定日志平铺器,默认为 DefaultFlattener
.build();
XLog.init( // 初始化 XLog
config, // 指定日志配置,如果不指定,会默认使用 new LogConfiguration.Builder().build()
androidPrinter, // 添加任意多的打印器。如果没有添加任何打印器,会默认使用 AndroidPrinter(Android)/ConsolePrinter(java)
consolePrinter,
filePrinter);
对于 android,做初始化的最佳地方是 Application.onCreate()。
3:全局用法
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);
... // 其他全局使用
4:局部用法:创建一个 Logger。
Logger partial = XLog.tag("PARTIAL-LOG")
... // 其他配置
.build();
然后对该 Logger 进行局部范围的使用,所有打印日志的相关方法都跟 XLog 类里的一模一样。
注意:这里的Logger区别 库:Logger(和我们下面说的另一个三方库 没一毛钱关系。)
partial.d("Simple message 1");
partial.d("Simple message 2");
... // 其他局部使用
迁移:
如果你有一个大工程正在用 Android Log(andorid API中的原生log),并且很难将所有对 Android Log 的使用都换成 XLog,那么你可以使用兼容 API,简单地将所有 ‘android.util.Log’ 替换成 ‘com.elvishew.xlog.XLog.Log’. (为了更好的性能,尽量不要使用兼容 API)
Android Studio迁移方式:
在 ‘Project’ 窗口里,切换到 ‘Project Files’ 标签,然后右键点击你的源码目录。 在出现的菜单里,点击 ‘Replace in Path…’ 选项。 在弹出的对话框里,’Text to find’ 区域填上 ‘android.util.Log’,’Replace with’ 区域填个 ‘com.elvishew.xlog.XLog.Log’,然后点击 ‘Find’。
最后不要忘记权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
OK——结束了 说的好像很详细了;但是 很遗憾……日志倒是显示的很完美,本人本地一直没有保存成功,不知道哪里的情况…
欢迎保存成功的同学告知一下操作 ,在这里谢过了…
下面我们上手Logger库;集成简单易懂。
logger库分析
不多说先上图:下图是两种输出日志的模板样式:
如下图1;个人运行后的样板:
如下图2;来自api文档中的样板:
简单集成:两步完成;
1:配置日志的显示适配:(放在全局Application中或者项目开始)
Logger.addLogAdapter(new AndroidLogAdapter());
2:在需要打印日志的地方 打印即可完成;
如:
Logger.d("我是测试---");
Logger.d("debug");
Logger.e("error");
Logger.w("warning");
Logger.v("verbose");
Logger.i("information");
Logger.wtf("wtf!!!!");
// Logger.json(JSON_CONTENT);
// Logger.xml(XML_CONTENT);
上面的基本上可以满足:能看日志了,,,
给自己一点要求
不过 大家肯定都是追求完美的人;这个太基础了;有瑕疵…
ok 我们来看看在有瑕疵的基础上在添加一些其他的内容:比如获取日志对应的类名、能快速点击定位到 日志位置、日志显示行数等
那么这就是下面要讨论的内容了 …接着看…
1:首先这里给出几个全局方法方便看接下来的分析对比:类:AppLog.java
//获取类名称
//不建议用这种方式;因为在混淆后的包里面出现的很可能是a/b/c等这种混淆后的输出信息;这样不便于定位问题
public static String getClassName() {
String result;
// 这里的数组的index2是根据你工具类的层级做不同的定义,这里仅仅是关键代码
StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[2];
result = thisMethodStack.getClassName();
int lastIndex = result.lastIndexOf(".");
result = result.substring(lastIndex + 1, result.length());
return result;
}
//获取类名称并添加上超链接
public static String callMethodAndLine() {
String result = "at ";
StackTraceElement thisMethodStack = (new Exception()).getStackTrace()[1];
result += thisMethodStack.getClassName()+ "."; // 当前的类名(全名)
result += thisMethodStack.getMethodName();
result += "(" + thisMethodStack.getFileName();
result += ":" + thisMethodStack.getLineNumber() + ") ";
return result;
}
public static void d1( String info) {
Logger.d(callMethodAndLine()+"\n"+info);
}
2:Application中初始化 Logger的配置参数:
public void ininLogger() {//初始化 放在项目的开始位置或者:Application 中的OnCreate中较为合适
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false) // (可选)是否显示线程信息。默认值为true
.methodCount(2) //(可选)要显示的方法行数。默认值2
.methodOffset(3) // (可选)隐藏内部方法调用到偏移量。默认5
.tag("MyLogger_Test") // (可选)每个日志的全局标签。默认PRETTY_LOGGER
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));//TJ:初始化 Logger日志适配
//TJ:打印日志策略:
Logger.addLogAdapter(new AndroidLogAdapter() {
@Override
public boolean isLoggable(int priority, String tag) {
return BuildConfig.DEBUG;//如果是Debug 模式 ;打印日志
}
});
}
3:打印日志的操作:
Log.d("Tag", "I'm a log which you don't see easily, hehe");
Log.d("json content", "{ \"key\": 3, \n \"value\": something}");
Log.d("error", "There is a crash somewhere or any warning");
// Logger.addLogAdapter(new AndroidLogAdapter());
Logger.d("我是测试---");
Logger.d("debug");
Logger.e("error");
Logger.w("warning");
Logger.v("verbose");
Logger.i("information");
Logger.wtf("wtf!!!!");
这样我们的日志显示就是下面这样式的:
如图:
说明:
这里的TAG是我们设置的:MyLogger_Test
要显示的方法行数:methodCount : 2
隐藏内部方法调用到偏移量:methodOffset :3
BuildConfig.DEBUG:Debug 模式打印日志
看着倒是好看了;还是不完美…
那么我们给打印的日志加上类/对应行/ Link链接:方法:callMethodAndLine()
1:为了区别:这里我们注释掉:Application中的配置;仅留下下面部分:
public void ininLogger() {//初始化 放在项目的开始位置或者:Application 中的OnCreate中较为合适
// FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
// .showThreadInfo(false) // (可选)是否显示线程信息。默认值为true
// .methodCount(2) //(可选)要显示的方法行数。默认值2
// .methodOffset(3) // (可选)隐藏内部方法调用到偏移量。默认5
// .tag("MyLogger_Test") // (可选)每个日志的全局标签。默认PRETTY_LOGGER
// .build();
//
// Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));//TJ:初始化 Logger日志适配
//TJ:打印日志策略:
Logger.addLogAdapter(new AndroidLogAdapter() {
@Override
public boolean isLoggable(int priority, String tag) {
return BuildConfig.DEBUG;//如果是Debug 模式 ;打印日志
}
});
}
2:在类:AppLog.java中添加全局配置方法:
public static void ininLogger(String getClassName,String getDatat) {
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false) // (可选)是否显示线程信息。默认值为true
.methodCount(2) //(可选)要显示的方法行数。默认值2
.methodOffset(3) // (可选)隐藏内部方法调用到偏移量。默认5
.tag(getClassName) // (可选)每个日志的全局标签。默认PRETTY_LOGGER
// .tag("MyLogger_Test") //对比 : Application中的设置默认;
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy));//TJ:初始化 Logger日志适配
//TJ:打印日志策略:
Logger.addLogAdapter(new AndroidLogAdapter() {
@Override public boolean isLoggable(int priority, String tag) {
return BuildConfig.DEBUG;//如果是Debug 模式 ;打印日志
}
});
Logger.d(getDatat);
}
3:执行日志打印如:
Log.d("Tag", "I'm a log which you don't see easily, hehe");
Log.d("json content", "{ \"key\": 3, \n \"value\": something}");
Log.d("error", "There is a crash somewhere or any warning");
// Logger.addLogAdapter(new AndroidLogAdapter());
Logger.d("我是测试---");
Logger.d("debug");
Logger.e("error");
Logger.w("warning");
Logger.v("verbose");
Logger.i("information");
Logger.wtf("wtf!!!!");
AppLog.ininLogger(AppLog.callMethodAndLine() ,"我是测试66 ");
AppLog.d1("我是测试日志");
4:打印的日志输出:
如图:
注意图片中的标注对比;
5:将日志保存到文件
Logger.addLogAdapter(new DiskLogAdapter());
将自定义标签添加到Csv格式策略
FormatStrategy formatStrategy = CsvFormatStrategy.newBuilder()
.tag("custom")
.build();
Logger.addLogAdapter(new DiskLogAdapter(formatStrategy));
=====本人使用的是默认的保存日志路径;保存到 了sd卡下的 logger文件夹 下 =====
OK 这就满足了我们上述说的 :能链接跳转到具体日志位置的操作了。
是不是有木有很简单的感觉 ? 恩 是的 ;很简单;这个库也很强大。
上面介绍的基本上能满足需求了; 在要说的 就看项目的情况了。
给出项目中可能用到的快捷方式:如TAG
快速生成TAG:
自动填写参数和方法名:
大家可以举一反三使用;如:loge/logd…等
欢迎大家学习交流…
你的点赞和评论是我们共同学习的见证!学习不晚。