好用高扩展性的Android平台日志框架Slog
Slog
GitHub项目地址:https://github.com/shenbibo/Slog
概述
Slog
是一个轻量级的Android
平台的日志库,其是基于对当前开源的日志框架Logger
和Timber
的一个组合与扩展。具有极大的可扩展性,相比于原生Android Log
,有以下新特性。
- 支持对日志的格式化排版输出,显示效果更清晰,更方便查看。
- 支持输出打印日志方法的栈和当前线程信息。
- 支持打印对象,支持自定义对象解析器,默认提供对数组,集合等解析。
- 支持使用多个自定义日志适配器,以决定日志的不同处理方式,默认提供
LogcatTree
适配器。 - 支持每次打印日志之前自定义日志输出配置,从而达到不同的日志输出效果。
- 支持自定义日志组装器,从而显示不同效果的格式化字符串。
引用方法
compile 'com.sky.slog:slog:0.4.0'
用法示例
初始化Slog
一般地在应用的Application
类的onCreate()
方法中调用如下类似代码。
Slog.init(new LogcatTree());
初始化时,至少需要传入一个日志适配器,当然我们也可以添加多个适配器,这个后面详解。
以上是最简单的初始化方式,我们还可以在初始化的时候对日志输出做全局配置,如下。
Slog.init(new LogcatTree()) // 初始化,设置适配器.showThreadInfo(true) // 设置是否打印日志的线程的信息.prefixTag("test") // 设置全局日志的前缀.logPriority(Slog.FULL) // 设置日志输出级别.methodCount(2) // 显示栈中方法的个数,默认从调用日志接口的方法往stack下计算.methodOffset(1) // 显示从调用日志打印接口的方法往stack下计算的偏移数.simpleMode(false); // 设置简单模式,无任何格式,等同于调用logcat,默认值为false.
以上方法的作用与默认值。
方法 | 默认值 | 作用 |
---|---|---|
prefixTag | “Android” | 设置全局日志前缀。 |
logPriority | Slog.FULL | 日志的输出级别,FULL表示可以输出任何级别的日志, NONE,表示不输出任何日志。 |
methodCount | 1 | 设置显示栈中方法到最终组装的日志中的个数,默认从调用日志接口的方法往stack下计算。 |
methodOffset | 0 | 设置从调用日志打印接口的方法往stack下的偏移数。 |
showThreadInfo | false | 设置是否打印日志的线程的信息。 |
simpleMode | false | 设置简单模式,无任何格式,不显示线程信息,方法调用,等同于调用logcat。 |
注意,上表中,如果simpleMode
为true
,则methodCount, methodOffset, showThreadInfo
将无效
另外我们还可以通过Slog.getSetting()
获取Setting
对象之后,在任何时候对全局Log输出配置项进行修改。
基本使用
使用以下方式初始化Slog
。
Slog.init(LogcatTree()).perfixTag("TestSlog").showThreadInfo(true);
打印普通日志
// 打印普通日志
Slog.d("sky debug");
Slog.i("sky info");// 打印格式化字符串
Slog.d("this is a format string log, str1 = %s, int value2 = %d, boolean3 = %b", "string1", 2, true);
打印错误日志
// 打印throwable
Slog.e(new Throwable());
Slog.w(new RuntimeException(), "test log with warn priority = %d", Slog.WARN);
打印json
和xml
json
和xml
字符串采用的日志级别都是Debug
的。
打印json字符串
String jsonEmpty = "";
String jsonEmpty2 = "{}";
String jsonNull = null;
Slog.json(jsonEmpty);
Slog.json(jsonEmpty2);
Slog.json(jsonNull);String json = "{'xyy1':[{'test1':'test1'},{'test2':'test2'}],'xyy2':{'test3':'test3','test4':'test4'}}";Slog.json(json);String jsonArray ="{ 'employees': [ {'firstName':'John', 'lastName':'Doe'}, {'firstName':'Anna', 'lastName':'Smith'}, "+ "{'firstName':'Peter', 'lastName':'Jones'}]}";
Slog.json(jsonArray);
打印xml
字符串
String androidXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +"<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +" package=\"com.sky.tools\" >\n" +"\n" +" <application\n" +" android:name=\".application.MainApplication\"\n" +" android:allowBackup=\"true\"\n" +" android:icon=\"@mipmap/ic_launcher\"\n" +" android:label=\"@string/app_name\"\n" +" android:roundIcon=\"@mipmap/ic_launcher_round\"\n" +" android:supportsRtl=\"true\"\n" +" android:theme=\"@style/AppTheme\" >\n" +" <activity android:name=\".main.MainActivity\" >\n" +" <intent-filter>\n" +" <action android:name=\"android.intent.action.MAIN\" />\n" +"\n" +" <category android:name=\"android.intent.category.LAUNCHER\" />\n" +" </intent-filter>\n" +" </activity>\n" +" </application>\n" +"\n" +"</manifest>";Slog.xml(androidXml);
打印对象
Slog
支持对对象的打印,其原理是给每个不同的对象类型添加对应的对象解析器,默认提供对数组,集合等解析,支持自定义对象解析器。
打印null
对象
Slog.i(null);
Slog.i("");
打印数组对象
// 全局多维对象数组
private static Object[] objectsArray = new Object[]{new boolean[]{false, true, true, false},new String[][]{new String[]{"22", "23", "24"},new String[]{"123", "456", "789"}},new int[][][]{new int[][]{new int[]{666, 555, 444},new int[]{111, 222, 333, 444}},new int[][]{new int[]{1, 2, 3, 4, 5, 6},new int[]{7878, 6565, 84155, 7542, 0}}}};// 打印对象数组
Object[] objectArray = new Object[1024];
for (int i = 0; i < objectArray.length; i++) {objectArray[i] = i;
}Slog.i(objectArray);// 打印String
String[] stringArray = new String[1024];
for (int i = 1024; i < stringArray.length + 1024; i++) {stringArray[i - 1024] = "is " + i;
}
Slog.i(stringArray);// 打印int数组
int[] intArray = new int[1024];
for (int i = 2048; i < intArray.length + 2048; i++) {intArray[i - 2048] = i;
}
Slog.i(intArray);// 打印多维数组
Slog.i(objectsArray);
打印自定义对象解析器的对象
有如下一个Student
类。
public class Student {private int number;private int age;private String name;private boolean isBoy;public Student(int number, int age, String name, boolean isBoy){this.number = number;this.age = age;this.name = name;this.isBoy = isBoy;}public String getName() {return name;}public int getAge() {return age;}public boolean isBoy() {return isBoy;}public int getNumber() {return number;}
}
自定义一个Student
对象解析器,所有的对象解析器都必须要实现Parser
接口。
public class StudentParser implements Parser<Student> {@Overridepublic Class<Student> getParseType() {return Student.class;}@Overridepublic String parseToString(Student student) {return student.getName() + " is a " + student.getAge() + " years old " + (student.isBoy() ? "boy" : "girl");}
}
上面的parseToString
将在解析对象时调用,由它返回被解析的对象最终要表示的字符串内容。
// 没有添加解析器之前
Student s = new Student(12345, 54, "sky", true);
Slog.d(s);// 添加解析器之后
Slog.addObjectParser(new StudentParser());
Slog.d(s);
打印Collection
对象
这里以Map
举例。
// empty map
Map<Integer, Student> map = new HashMap<>();
Slog.d(map);// int map
Map<Integer, Integer> intMap = new ConcurrentHashMap<>();
intMap.put(1, 2);
intMap.put(1543, 2745867);
intMap.put(17687, 27678);
intMap.put(76781, 27678);
intMap.put(1786768, 26786);
Slog.d(intMap);// Object Map
Map<Object, String> objectStringMap = new LinkedHashMap<>();
objectStringMap.put(new Object(), "11223786");
objectStringMap.put(new Object(), "475775486");
objectStringMap.put(new Object(), "7856874757");
Slog.d(objectStringMap);// student Map
map.put(12345, new Student(12345, 54, "sky", true));
map.put(123456, new Student(123456, 56, "sky2", true));
map.put(1234567, new Student(1234567, 15, "sky3", true));
map.put(12345678, new Student(12345678, 25, "sky4", true));
map.put(1234555, new Student(1234555, 35, "sky5", true));
map.put(12345444, new Student(12345444, 45, "sky6", true));
Slog.d(map);// map itself
Map map1 = new Hashtable<>();
//noinspection CollectionAddedToSelf,unchecked
map1.put(map1, map1);
Slog.d(map1);
指定临时的配置项打印日志
前面的示例都是不修改输出配置项时打印的日志,但是在很多时候我们可能根据不同的场景,需要采用不同的日志输出配置。
除了前面说的使用Slog.getSetting()
方法获取Setting
对象之后,修改全局输出配置外,Slog
框架还支持指定临时输出配置,只在下次当前线程打印日志时生效一次的方式更改日志输出效果。
具体包括以下几个方法。
Slog.t() // 指定下一次当前线程打印日志的tag,最终输出的日志的Tag组合为: prefixTag-tag
Slog.th() // 指定下一次当前线程打印日志是否显示线程信息
Slog.m() // 指定下一次当前线程打印日志显示的调用stack中方法的数目
Slog.o() // 指定下一次当前线程打印日志显示stack方法时的偏移值
Slog.s() // 指定下一次当前线程打印日志是否采用简单模式输出
上面的配置方法可以单个使用也可以组合使用。
单个使用
Slog.t("custom22").i("set tag to custom");
Slog.th(false).i("hide the threadInfo");
Slog.m(0).i("test 0 method count print, so hide track");
Slog.m(3).i("test three method count println");
Slog.o(1).i("method offset 1");
Slog.s(true).i("set to simple mode");
联合使用
Slog.s(false).t("fiveSetting").th(true).m(5).o(2).i("this time set five temp setting for test");
添加日志适配器
Slog
框架目前只提供一个实现的日志适配器LogcatTree
,本框架支持自定义日志适配器,所有的日志适配器都必须要继承Tree
抽象类或者其子类,为了保证足够的扩展性,我们在Tree
的接口中除了可以接收到封装处理好的日志之外,也可以对原始的日志数据进行处理。
自定义一个FileTree
。
public class FileTree extends Tree {// ... 还有其他的方法也可以根据需要复写// 处理对象类型的日志,注意该接口方法,也可以根据原始的`originalObject`参数进行自定义处理@Overrideprotected void prepareObjectLog(int priority, String tag, String[] compoundMessages, @Nullable Object originalObject) {super.prepareObjectLog(priority, tag, compoundMessages, originalObject);}// 处理String类型的日志,注意该接口方法,也可以根据原始的`originalMessage`参数进行自定义处理@Overrideprotected void prepareStringLog(int priority, String tag, Throwable t, String[] compoundMessages, @Nullable String originalMessages, @Nullable Object... args) {super.prepareStringLog(priority, tag, t, compoundMessages, originalMessages, args);}// 该方法为必须要实现的父类抽象方法@Overrideprotected void log(int priority, String tag, String message) {// ... 省略代码将日志保存到文件中}
}
将其添加到日志适配器列表中,以后就可以正常使用了。
java
Slog.plantTree(new FileTree());
注意: 在每个日志适配器中,我们可以根据需要最终自己确定将组装之后的日志或者原始日志如何处理。
自定义日志组装器
通过继承LogAssembler
抽象类,我们可以实现自己定义的日志组装器。
public class SimpleLogAssembler extends LogAssembler {@Overrideprotected void onFormatModeLogMethodStackTrace(List<String> compoundMessagesList, int methodCount, int stackOffset,StackTraceElement[] trace) {for (int i = methodCount; i > 0; i--) {int stackIndex = i + stackOffset;if (stackIndex >= trace.length) {continue;}//noinspection StringBufferReplaceableByStringStringBuilder builder = new StringBuilder();builder.append(getSimpleClassName(trace[stackIndex].getClassName())).append(".").append(trace[stackIndex].getMethodName()).append("(").append(trace[stackIndex].getFileName()).append(":").append(trace[stackIndex].getLineNumber()).append(")");compoundMessagesList.add(builder.toString());}}@Overrideprotected void onFormatModeLogThreadInfo(List<String> compoundMessagesList, Thread curThread) {compoundMessagesList.add(Helper.createThreadInfo(curThread));}@Overrideprotected void onFormatModeLogContent(List<String> compoundMessagesList, Throwable t, Object originalObject, Object[] args) {String[] compoundMessages = compoundMessage(t, originalObject, args);for (String compoundMessage : compoundMessages) {String[] splitMessages = compoundMessage.split(LINE_SEPARATOR);Collections.addAll(compoundMessagesList, splitMessages);}}
}
以上三个方法是子类必须要实现的,还有其他的方法子类可以选择性的复写。
以下是调用实例。
Slog.setLogAssembler(new SimpleLogAssembler());
Slog.i("set log assembler to simple");
Slog.m(10).i("simple log assembler set methodCount to 10");// 设置为null,将使用默认的日志组装器
Slog.setLogAssembler(null);
Slog.i("Slog.setLogAssembler(null), so turn to default log assembler");
另外,我们也可以在初始化时调用Slog.init(Tree, LogAssembler)
方法时进行指定日志组装器。
SlogTest
测试用例集
更多的用法可以参考slog/src/androidTest/java/com.sky.slog/SlogTest.java
。
结构概述
Slog打印日志的基本流程可以归纳为以下几个步骤。
- 打印日志,调用对应的
Slog
接口。 - 根据当前日志全局配置,判断是否对需要输出日志(当前是只判断允许输出的日志级别
Priority
)。 - 结合全局日志配置和单次指定的日志配置(单次优先级高于全局),对原始日志进行组装。
- 将组装好的日志和原始日志数据通过日志分发器分发到各个日志适配器。
- 每个日志适配器最终根据自身实现对日志进行处理。
简单的流程图。
简单的类图。
LogAssembler
,日志组装器的抽象类,负责对日志进行组装,调用分发器将组装好的日志进行分发。LogDispatcher
,日志分发器接口。LogController
,分别实现了TreeManger
,LogDispatcher
接口,通过其分发日志功能,将日志分发到其管理的日志适配器中。
致谢
本库最终形成,分别参考了以下三个库,本库的设计借鉴了它们的设计思想与代码实现,十分感谢。
Logger
: https://github.com/orhanobut/logger
Timber
: https://github.com/JakeWharton/timber
ViseLog
: https://github.com/xiaoyaoyou1212/ViseLog
好用高扩展性的Android平台日志框架Slog相关推荐
- aws s3仅允许cloudfront访问_初创公司如何用AWS搭建高扩展性架构
亚信云天的理解 • 初创公司需要快.多.好.省的技术架构 o 快:针对业务需要可以快速获得资源与服务 o 多:拥有丰富的云服务可供选择,能不自己做就不自己做 o 好:强调扩展性和高可用,既不 ...
- 【总结】初创公司用AWS搭建高扩展性架构
下载地址:完整mp4视频 演讲人:张侠 博士 1. 邱洋的理解 初创公司需要快.多.好.省的技术架构 快:针对业务需要可以快速获得资源与服务 多:拥有丰富的云服务可供选择,能不自己做就不自己做 好:强 ...
- 高性能、高并发、高扩展性和可读性的网络服务器架构:StateThreads
译文在后面:代码我放在GITHUB了:http://github.com/ossrs/state-threads. State Threads for Internet Applications In ...
- 优秀开源项目之三:高性能、高并发、高扩展性和可读性的网络服务器架构State Threads...
译文在后面. State Threads for Internet Applications Introduction State Threads is an application library ...
- 如何构建高扩展性网站?
本篇通过阅读<高扩展性网站的50条原则>,总结出以下内容. 一方面博主没有实际的架构经验,另一方面知识面也不够宽阔,所以只能系统的总结书中的要点,并根据自己的理解做些归纳. 主要内容 本书 ...
- Fastsocket:高扩展性的 Socket 以及 Linux 内核的底层网络实现
目录 简介 安装编译 测试结果 GitHub README 介绍 参加者 安装 从源安装 开关内核 系统配置 用法 合适的场景### 如何使用 演示服务器 评价 Nginx的 HAProxy 在线评估 ...
- 如何构建和设计高扩展性系统
主要内容 本书从多个方面围绕高扩展性提出了50条建议,一个高扩展性的网站会随着业务的发展.用户的增加,自由的扩展架构,从而轻松的应付网站的快速发展.下面看看本书的具体内容: 化简方程 1 不要过度的设 ...
- Android平台日志收集系统
Android平台日志收集系统 在产品开发测试中以及产品投放到终端客户后,我们经常会遇到各种各样的问题,产品出异常,比较严重的就是使用过程中死机,用户无法操作.对于这种情况,将问题反馈给研发,问题能够 ...
- 日志框架 android,深度剖析Android JDK 日志框架
全球为数众多的移动电话用户从未使用过任何基于AndroidJDK 日志框架,谷歌的目标是让不依赖于设备甚至平台,希望大家看完本文能给大家带来相关帮助. 日志记录对于软件的维护特别是对于已部署到运行环境 ...
最新文章
- Xcode中如何解决无法使用svn命令行的问题
- android 自定义radiobutton 样式,RadioButton自定义点击时的背景颜色
- 《面向模式的软件体系结构2-用于并发和网络化对象模式》读书笔记(7)--- 事件处理模式与反应器...
- 网页性能管理详解:浅谈chrome-Timeline及window.requestAnimationFrame()方法
- StringBuilder 、StringBuffer 、 String
- J2SE综合对java util的总结 一
- 差分隐私 python_[宜配屋]听图阁
- explicitWidth与width,和用setActualSize()方法有什么区别?
- linux procs单位m,Linux
- React Native : 自定义视图
- php.js 文件下载,使用JavaScript开始下载文件
- 使用Elasticsearch进行高效的中文搜索
- LeetCode-----第139题-----单词拆分
- 高性能信号发生器介绍
- 自然语言生成技术现状调查:核心任务、应用和评估(2)
- TeamViewer固定密码远程连接
- 英文SEO移动端搜索优化指南
- 从事人工智能行业,学什么专业比较好,需要学哪些基础知识
- 使用html设计商业网站试题,电子商务师网页设计与制作试题
- 从携程信用卡信息泄露事件谈网上支付安全