├── .gitignore
├── .idea
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── gradle.xml
├── jarRepositories.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── allens
│ │ └── xloghelper
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── allens
│ │ │ └── xloghelper
│ │ │ ├── MainActivity.kt
│ │ │ └── MyApp.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── layout
│ │ └── activity_main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── allens
│ └── xloghelper
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── xlog
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── libs
└── armeabi-v7a
│ ├── libc++_shared.so
│ └── libmarsxlog.so
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── allens
│ └── xlog
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
└── java
│ └── com
│ ├── allens
│ └── xlog
│ │ ├── Builder.kt
│ │ ├── CustomXLog.kt
│ │ ├── LogLevel.kt
│ │ ├── LogModel.kt
│ │ └── XLogHelper.kt
│ └── tencent
│ └── mars
│ └── xlog
│ ├── Log.java
│ └── Xlog.java
└── test
└── java
└── com
└── allens
└── xlog
└── ExampleUnitTest.kt
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | xmlns:android
33 |
34 | ^$
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | xmlns:.*
44 |
45 | ^$
46 |
47 |
48 | BY_NAME
49 |
50 |
51 |
52 |
53 |
54 |
55 | .*:id
56 |
57 | http://schemas.android.com/apk/res/android
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | .*:name
67 |
68 | http://schemas.android.com/apk/res/android
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 | name
78 |
79 | ^$
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | style
89 |
90 | ^$
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | .*
100 |
101 | ^$
102 |
103 |
104 | BY_NAME
105 |
106 |
107 |
108 |
109 |
110 |
111 | .*
112 |
113 | http://schemas.android.com/apk/res/android
114 |
115 |
116 | ANDROID_ATTRIBUTE_ORDER
117 |
118 |
119 |
120 |
121 |
122 |
123 | .*
124 |
125 | .*
126 |
127 |
128 | BY_NAME
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | 基于微信XLog的日志框架&&对于XLog的分析
3 |
4 |
5 |
6 | > 建议在 [个人博客](https://allens.icu/posts/60625924/#more) 中查看,阅读体验更佳
7 |
8 | # 前言
9 |
10 | 之前写过一个 日志框架[LogHelper](https://github.com/JiangHaiYang01/LogHelper) ,是基于 Logger 开源库封装的,当时的因为项目本身的日志不是很多,完全可以使用,最近和其他公司合作,在一个新的项目上反馈,说在 大量log 的情况下会影响到手机主体功能的使用。从而让我对之前的日志行为做了一个深刻的反省
11 |
12 |
13 | 随后在开发群中咨询了其他开发的小伙伴,如果追求性能,可以研究一下 微信的 xlog ,也是本篇博客的重点
14 |
15 |
16 | # xlog 是什么
17 |
18 | xlog 是什么 这个问题 我这也是在[【腾讯Bugly干货分享】微信mars 的高性能日志模块 xlog](https://zhuanlan.zhihu.com/p/23879436)得到了答案
19 |
20 | 简单来说 ,就是腾讯团队分享的基于 c/c++ 高可靠性高性能的运行期日志组件
21 |
22 |
23 |
24 | # 官网的 sample
25 |
26 | 知道了他是什么,就要只要他是怎么用的,打开github 找到官网[Tencent/mars](https://github.com/Tencent/mars)
27 |
28 | 使用非常简单
29 |
30 | ## 下载库
31 |
32 | ```
33 | dependencies {
34 | compile 'com.tencent.mars:mars-xlog:1.2.3'
35 | }
36 | ```
37 |
38 | ## 使用
39 |
40 | ```
41 | System.loadLibrary("c++_shared");
42 | System.loadLibrary("marsxlog");
43 |
44 | final String SDCARD = Environment.getExternalStorageDirectory().getAbsolutePath();
45 | final String logPath = SDCARD + "/marssample/log";
46 |
47 | // this is necessary, or may crash for SIGBUS
48 | final String cachePath = this.getFilesDir() + "/xlog"
49 |
50 | //init xlog
51 | if (BuildConfig.DEBUG) {
52 | Xlog.appenderOpen(Xlog.LEVEL_DEBUG, Xlog.AppenderModeAsync, cachePath, logPath, "MarsSample", 0, "");
53 | Xlog.setConsoleLogOpen(true);
54 |
55 | } else {
56 | Xlog.appenderOpen(Xlog.LEVEL_INFO, Xlog.AppenderModeAsync, cachePath, logPath, "MarsSample", 0, "");
57 | Xlog.setConsoleLogOpen(false);
58 | }
59 |
60 | Log.setLogImp(new Xlog());
61 | ```
62 |
63 | OK 实现了他的功能
64 |
65 | > 不要高兴的太早,后续的问题都头大
66 |
67 |
68 | # 分析各个方法的作用
69 |
70 |
71 | 知道了最简单的用法,就想看看他支持哪些功能
72 |
73 | 按照官网的demo 首先分析一下``appenderOpen``
74 |
75 | ## appenderOpen(int level, int mode, String cacheDir, String logDir, String nameprefix, int cacheDays, String pubkey)
76 |
77 | ### level
78 |
79 | 日志级别 没啥好说的 XLog 中已经写得很清楚了
80 |
81 |
82 | ```
83 | public static final int LEVEL_ALL = 0;
84 | public static final int LEVEL_VERBOSE = 0;
85 | public static final int LEVEL_DEBUG = 1;
86 | public static final int LEVEL_INFO = 2;
87 | public static final int LEVEL_WARNING = 3;
88 | public static final int LEVEL_ERROR = 4;
89 | public static final int LEVEL_FATAL = 5;
90 | public static final int LEVEL_NONE = 6;
91 | ```
92 |
93 |
94 | > 值得注意的地方 debug 版本下建议把控制台日志打开,日志级别设为 Verbose 或者 Debug, release 版本建议把控制台日志关闭,日志级别使用 Info.
95 |
96 | 这个在官网的 [接入指南](https://github.com/Tencent/mars/wiki/Mars-Android-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97)
97 |
98 |
99 | 这里也可以使用
100 |
101 | ```java
102 | public static native void setLogLevel(int logLevel);
103 | ```
104 | 方法设置
105 |
106 | ### mode
107 |
108 | 写入的模式
109 |
110 | - public static final int AppednerModeAsync = 0;
111 |
112 | 异步写入
113 |
114 | - public static final int AppednerModeSync = 1;
115 |
116 | 同步写入
117 |
118 |
119 | 同步写入,可以理解为实时的日志,异步则不是
120 |
121 | > Release版本一定要用 AppednerModeAsync, Debug 版本两个都可以,但是使用 AppednerModeSync 可能会有卡顿
122 |
123 | 这里也可以使用
124 |
125 | ```java
126 | public static native void setAppenderMode(int mode);
127 | ```
128 | 方法设置
129 |
130 | ### cacheDir 设置缓存目录
131 |
132 |
133 |
134 | 缓存目录,当 logDir 不可写时候会写进这个目录,可选项,不选用请给 "", 如若要给,建议给应用的 /data/data/packname/files/log 目录。
135 |
136 | 会在目录下生成后缀为 .mmap3 的缓存文件,
137 |
138 |
139 | ### logDir 设置写入的文件目录
140 |
141 | 真正的日志,后缀为 .xlog
142 |
143 |
144 | > 日志写入目录,请给单独的目录,除了日志文件不要把其他文件放入该目录,不然可能会被日志的自动清理功能清理掉。
145 |
146 | ### nameprefix 设置日志文件名的前缀
147 |
148 | 日志文件名的前缀,例如该值为TEST,生成的文件名为:TEST_20170102.xlog。
149 |
150 | ### cacheDays
151 |
152 | 一般情况下填0即可。非0表示会在 _cachedir 目录下存放几天的日志。
153 |
154 | 这里的描述比较晦涩难懂,当我设置这个参数非0 的时候 会发现 原本设置在 logDir 目录下的文件 出现在了 cacheDir
155 |
156 | 例如 正常应该是
157 |
158 |
159 | 文件结构
160 |
161 | ```java
162 | - cacheDir
163 | - log.mmap3
164 | - logDir
165 | - log_20200710.xlog
166 | - log_20200711.xlog
167 | ```
168 |
169 | 变成这样
170 |
171 | ```java
172 | - cacheDir
173 | - log.mmap3
174 | - log_20200710.xlog
175 | - log_20200711.xlog
176 | - logDir
177 |
178 | ```
179 |
180 | 全部到了 cacheDir 下面
181 |
182 | > cacheDays 的意思是 在多少天以后 从缓存目录移到日志目录
183 |
184 |
185 | ### pubkey 设置加密的 pubkey
186 |
187 | 这里涉及到了日志的加密与解密,下面会专门介绍
188 |
189 |
190 | ## setMaxFileSize 设置文件大小
191 |
192 | 在 Xlog 下有一个 native 方法
193 |
194 | ```
195 | public static native void setMaxFileSize(long size);
196 | ```
197 |
198 | 他表示 最大文件大小,这里需要说一下,原本的默认设置 是一天一个日志文件在 [appender.h](https://github.com/Tencent/mars/blob/master/mars/log/appender.h) 描述的很清楚
199 |
200 | ```java
201 | /*
202 | * By default, all logs will write to one file everyday. You can split logs to multi-file by changing max_file_size.
203 | *
204 | * @param _max_byte_size Max byte size of single log file, default is 0, meaning do not split.
205 | */
206 | void appender_set_max_file_size(uint64_t _max_byte_size);
207 | ```
208 |
209 | 默认情况下,所有日志每天都写入一个文件。可以通过更改max_file_size将日志分割为多个文件。单个日志文件的最大字节大小,默认为0,表示不分割
210 |
211 |
212 |
213 | 当超过设置的文件大小以后。文件会变成如下目录结构
214 |
215 |
216 | ```java
217 | - cacheDir
218 | - log.mmap3
219 | - logDir
220 | - log_20200710.xlog
221 | - log_20200710_1.xlog
222 | - log_20200710_2.xlog
223 | ```
224 |
225 |
226 | 在 [appender.cc](https://github.com/Tencent/mars/blob/master/mars/log/src/appender.cc) 对应的有如下逻辑,
227 |
228 | ```
229 | static long __get_next_fileindex(const std::string& _fileprefix, const std::string& _fileext) {
230 | ...
231 | return (filesize > sg_max_file_size) ? index + 1 : index;
232 |
233 | ```
234 |
235 |
236 | ## setConsoleLogOpen 设置是否在控制台答应日志
237 |
238 | ···java
239 | public static native void setConsoleLogOpen(boolean isOpen);
240 | ···
241 |
242 | 设置是否在控制台答应日志
243 |
244 | ## setErrLogOpen
245 |
246 | 这个方法是没用的,一开始以为哪里继承的有问题,在查看源码的时候发现 他是一个空方法,没有应用
247 |
248 | 
249 |
250 | 使用的话会导致程序异常,在自己编译的so 中我就把它给去掉了
251 |
252 | ## setMaxAliveTime 设置单个文件最大保留时间
253 |
254 | ```java
255 | public static native void setMaxAliveTime(long duration);
256 | ```
257 |
258 |
259 | 置单个文件最大保留时间 单位是秒,这个方法有3个需要注意的地方,
260 |
261 | - 必须在 appenderOpen 方法之前才有效
262 | - 最小的时间是 一天
263 | - 默认的时间是10天
264 |
265 | 在 [appender.cc](https://github.com/Tencent/mars/blob/master/mars/log/src/appender.cc) 中可以看到
266 |
267 | ```
268 | static const long kMaxLogAliveTime = 10 * 24 * 60 * 60; // 10 days in second
269 | static const long kMinLogAliveTime = 24 * 60 * 60; // 1 days in second
270 | static long sg_max_alive_time = kMaxLogAliveTime;
271 |
272 | ....
273 |
274 |
275 | void appender_set_max_alive_duration(long _max_time) {
276 | if (_max_time >= kMinLogAliveTime) {
277 | sg_max_alive_time = _max_time;
278 | }
279 | }
280 | ```
281 |
282 | 默认的时间是10天
283 |
284 | ## appenderClose
285 |
286 | 在 [文档](https://github.com/Tencent/mars#mars_cn)中介绍说是在 程序退出时关闭日志 调用appenderClose的方法
287 |
288 | 然而在实际情况中 Application 类的 onTerminate() 只有在模拟器中才会生效,在真机中无效的,
289 |
290 | 如果在程序退出的时候没有触发 appenderClose 那么在下一次启动的时候,xlog 也会把日志写入到文件中
291 |
292 |
293 | 所以如何触发呢?
294 |
295 | 建议尽可能的去触发他 例如用户双击back 退出的情况下 你肯定是知道的
296 | 如果放在后台被杀死了,这个时候也真的没办法刷新,也没关系,上面也说了,再次启动的时候会刷新到日志中,
297 |
298 | ## appenderFlush
299 |
300 |
301 | 当日志写入模式为异步时,调用该接口会把内存中的日志写入到文件。
302 |
303 | isSync : true 为同步 flush,flush 结束后才会返回。
304 | isSync : false 为异步 flush,不等待 flush 结束就返回。
305 |
306 |
307 | # 日志文件的加密
308 |
309 | 这一块单独拿出来说明,是因为之前使用上遇到了坑
310 |
311 | 首先是这个 入参 PUB_KEY,一脸懵,是个啥,
312 |
313 | 在 [mars/blob/master/mars/log/crypt/gen_key.py](https://github.com/Tencent/mars/blob/master/mars/log/crypt/gen_key.py) 这个就是能够获取到 PUB_KEY 的方法
314 |
315 | 运行如下
316 |
317 | ```python
318 | $ python gen_key.py
319 | WARNING: Executing a script that is loading libcrypto in an unsafe way. This will fail in a future version of macOS. Set the LIBRESSL_REDIRECT_STUB_ABORT=1 in the environment to force this into an error.
320 | save private key
321 | 471e607b1bb3760205f74a5e53d2764f795601e241ebc780c849e7fde1b4ce40
322 |
323 | appender_open's parameter:
324 | 300330b09d9e771d6163bc53a4e23b188ac9b2f5c7150366835bce3a12b0c8d9c5ecb0b15274f12b2dffae7f4b11c3b3d340e0521e8690578f51813c93190e1e
325 | ```
326 |
327 |
328 | 上面的 private key 自己保存好
329 |
330 | appender_open's parameter: 就是需要的 PUB_KEY
331 |
332 |
333 | # 日志文件的解密
334 |
335 |
336 | 上面已经知道如何加密了,现在了解一下如何解密
337 |
338 | ## 下载pyelliptic1
339 |
340 | 在[Xlog 加密使用指引](https://github.com/Tencent/mars/wiki/Xlog-%E5%8A%A0%E5%AF%86%E4%BD%BF%E7%94%A8%E6%8C%87%E5%BC%95)中能够看到
341 |
342 | 需要下载 [pyelliptic1.5.7](https://github.com/yann2192/pyelliptic/releases/tag/1.5.7)
343 | 然后编译 否则下面的命令会失败
344 |
345 |
346 |
347 | ## 直接解密脚本
348 |
349 |
350 |
351 | xlog 很贴心的给我们提供了两个脚本
352 |
353 | 使用 [decode_mars_nocrypt_log_file.py](https://github.com/Tencent/mars/blob/master/mars/log/crypt/decode_nomars_crypt_log_file.py) 解压没有加密的
354 |
355 | ```python
356 | python decode_mars_nocrypt_log_file [path]
357 | ```
358 |
359 | 使用 [decode_mars_crypt_log_file.py](https://github.com/Tencent/mars/blob/master/mars/log/crypt/decode_mars_crypt_log_file.py) 加密的文件
360 |
361 | 在使用之前需要将 脚本中的
362 |
363 | ```
364 | PRIV_KEY = "145aa7717bf9745b91e9569b80bbf1eedaa6cc6cd0e26317d810e35710f44cf8"
365 | PUB_KEY = "572d1e2710ae5fbca54c76a382fdd44050b3a675cb2bf39feebe85ef63d947aff0fa4943f1112e8b6af34bebebbaefa1a0aae055d9259b89a1858f7cc9af9df1"
366 | ```
367 |
368 | 改成上面自己获取到的 key 否则是解压不出来的
369 |
370 | ```python
371 | python decode_mars_crypt_log_file.py ~/Desktop/log/log_20200710.xlog
372 | ```
373 |
374 | 直接生成一个
375 |
376 | ```java
377 | - cacheDir
378 | - log.mmap3
379 | - logDir
380 | - log_20200710.xlog
381 | - log_20200710.xlog.log
382 | ```
383 |
384 | 也可以自定义名字
385 |
386 | ```python
387 | python decode_mars_crypt_log_file.py ~/Desktop/log/log_20200710.xlog ~/Desktop/log/1.log
388 | ```
389 |
390 | ```java
391 | - cacheDir
392 | - log.mmap3
393 | - logDir
394 | - log_20200710.xlog
395 | - 1.log
396 | ```
397 |
398 | # 修改日志的格式
399 |
400 | 打开我们解压好的日志查看
401 |
402 | ```
403 | ^^^^^^^^^^Oct 14 2019^^^20:27:59^^^^^^^^^^[17223,17223][2020-07-24 +0800 09:49:19]
404 | get mmap time: 3
405 | MARS_URL:
406 | MARS_PATH: master
407 | MARS_REVISION: 85b19f92
408 | MARS_BUILD_TIME: 2019-10-14 20:27:57
409 | MARS_BUILD_JOB:
410 | log appender mode:0, use mmap:1
411 | cache dir space info, capacity:57926635520 free:52452691968 available:52452691968
412 | log dir space info, capacity:57926635520 free:52452691968 available:52452691968
413 | [I][2020-07-24 +8.0 09:49:21.179][17223, 17223][TAG][, , 0][======================> 1
414 | [I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 2
415 | [I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 3
416 | [I][2020-07-24 +8.0 09:49:21.180][17223, 17223][TAG][, , 0][======================> 4
417 | [I][2020-07-24 +8.0 09:49:21.181][17223, 17223][TAG][, , 0][======================> 5
418 | [I][2020-07-24 +8.0 09:49:21.181][17223, 17223][TAG][, , 0][======================> 6
419 | [I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 7
420 | [I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 8
421 | [I][2020-07-24 +8.0 09:49:21.182][17223, 17223][TAG][, , 0][======================> 9
422 | [I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 10
423 | [I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 11
424 | [I][2020-07-24 +8.0 09:49:21.183][17223, 17223][TAG][, , 0][======================> 12
425 | [I][2020-07-24 +8.0 09:49:21.184][17223, 17223][TAG][, , 0][======================> 13
426 | [I][2020-07-24 +8.0 09:49:21.184][17223, 17223][TAG][, , 0][======================> 14
427 | [I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 15
428 | [I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 16
429 | [I][2020-07-24 +8.0 09:49:21.185][17223, 17223][TAG][, , 0][======================> 17
430 | ```
431 |
432 | 我擦泪 除了我们需要的信息以外,还有这么多杂七杂八的信息,如何去掉,并且自己定义一下格式
433 |
434 |
435 | 这里就需要自己去编译 so 了,好在 xlog 已经给我们提供了很好的编译代码
436 |
437 | 对应的文档 [本地编译](https://github.com/Tencent/mars/wiki/Mars-Android-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97#local_compile)
438 |
439 |
440 | > 对于编译这块按照文档来就好了 需要注意的是
441 |
442 | - 一定要用 ndk-r20 不要用最新版本的 21
443 | - 一定用 Python2.7 mac 自带 不用要 Python3
444 |
445 |
446 | ## 去掉头文件
447 |
448 | 首先我们去到这个头文件,对于一个日志框架来着,这个没啥用
449 |
450 | ```
451 | ^^^^^^^^^^Oct 14 2019^^^20:27:59^^^^^^^^^^[17223,17223][2020-07-24 +0800 09:49:19]
452 | get mmap time: 3
453 | MARS_URL:
454 | MARS_PATH: master
455 | MARS_REVISION: 85b19f92
456 | MARS_BUILD_TIME: 2019-10-14 20:27:57
457 | MARS_BUILD_JOB:
458 | log appender mode:0, use mmap:1
459 | cache dir space info, capacity:57926635520 free:52452691968 available:52452691968
460 | log dir space info, capacity:57926635520 free:52452691968 available:52452691968
461 | ```
462 |
463 | 在本机下载好的 mars 下,找到 [appender.cc](https://github.com/Tencent/mars/blob/master/mars/log/src/appender.cc) 将头文件去掉
464 |
465 | 
466 |
467 |
468 | ## 修改日志格式
469 |
470 | 默认的格式很长
471 |
472 | ```
473 | [I][2020-07-24 +8.0 09:49:21.179][17223, 17223][TAG][, , 0][======================> 1
474 | ```
475 | [日志级别][时间][pid,tid][tag][filename,strFuncName,line][日志内容
476 |
477 | 是一个这样结构
478 |
479 | 比较乱,我们想要的日志 就时间,级别,日志内容 就行了
480 |
481 | 找到 [formater.cc](https://github.com/Tencent/mars/blob/master/mars/log/src/formater.cc)
482 |
483 | 将原本的
484 |
485 | ```java
486 | int ret = snprintf((char*)_log.PosPtr(), 1024, "[%s][%s][%" PRIdMAX ", %" PRIdMAX "%s][%s][%s, %s, %d][", // **CPPLINT SKIP**
487 | _logbody ? levelStrings[_info->level] : levelStrings[kLevelFatal], temp_time,
488 | _info->pid, _info->tid, _info->tid == _info->maintid ? "*" : "", _info->tag ? _info->tag : "",
489 | filename, strFuncName, _info->line);
490 | ```
491 |
492 | 改成
493 |
494 | ```java
495 | int ret = snprintf((char*)_log.PosPtr(), 1024, "[%s][%s]", // **CPPLINT SKIP**
496 | temp_time, _logbody ? levelStrings[_info->level] : levelStrings[kLevelFatal] );
497 | ```
498 |
499 | 就行了
500 |
501 | 然后从新编译,将so 翻入项目 在看一下现在的效果
502 |
503 | ```java
504 | [2020-07-24 +8.0 11:47:42.597][I]======================>9
505 | ```
506 |
507 | ok 打完收工
508 |
509 |
510 | # 简单的封装一下
511 |
512 | 基本上分析和实现了我们需要的功能,那么把这部分简单的封装一下
513 |
514 | 放上核心的 Builder 源码可在下面自行查看
515 |
516 | ```java
517 | package com.allens.xlog
518 |
519 | import android.content.Context
520 | import com.tencent.mars.xlog.Log
521 | import com.tencent.mars.xlog.Xlog
522 |
523 | class Builder(context: Context) {
524 |
525 | companion object {
526 | //日志的tag
527 | var tag = "log_tag"
528 | }
529 |
530 | //是否是debug 模式
531 | private var debug = true
532 |
533 |
534 | //是否打印控制台日志
535 | private var consoleLogOpen = true
536 |
537 |
538 | //是否每天一个日志文件
539 | private var oneFileEveryday = true
540 |
541 | //默认的位置
542 | private val defCachePath = context.getExternalFilesDir(null)?.path + "/mmap"
543 |
544 | // mmap 位置 默认缓存的位置
545 | private var cachePath = defCachePath
546 |
547 | //实际保存的log 位置
548 | private var logPath = context.getExternalFilesDir(null)?.path + "/logDir"
549 |
550 | //文件名称前缀 例如该值为TEST,生成的文件名为:TEST_20170102.xlog
551 | private var namePreFix = "log"
552 |
553 | //写入文件的模式
554 | private var model = LogModel.Async
555 |
556 | //最大文件大小
557 | //默认情况下,所有日志每天都写入一个文件。可以通过更改max_file_size将日志分割为多个文件。
558 | //单个日志文件的最大字节大小,默认为0,表示不分割
559 | // 最大 当文件不能超过 10M
560 | private var maxFileSize = 0L
561 |
562 | //日志级别
563 | //debug 版本下建议把控制台日志打开,日志级别设为 Verbose 或者 Debug, release 版本建议把控制台日志关闭,日志级别使用 Info.
564 | private var logLevel = LogLevel.LEVEL_INFO
565 |
566 | //通过 python gen_key.py 获取到的公钥
567 | private var pubKey = ""
568 |
569 | //单个文件最大保留时间 最小 1天 默认时间 10天
570 | private var maxAliveTime = 10
571 |
572 | //缓存的天数 一般情况下填0即可。非0表示会在 _cachedir 目录下存放几天的日志。
573 | //原来缓存日期的意思是几天后从缓存目录移到日志目录
574 | private var cacheDays = 0
575 |
576 | fun setCachePath(cachePath: String): Builder {
577 | this.cachePath = cachePath
578 | return this
579 | }
580 |
581 | fun setLogPath(logPath: String): Builder {
582 | this.logPath = logPath
583 | return this
584 | }
585 |
586 |
587 | fun setNamePreFix(namePreFix: String): Builder {
588 | this.namePreFix = namePreFix
589 | return this
590 | }
591 |
592 | fun setModel(model: LogModel): Builder {
593 | this.model = model
594 | return this
595 | }
596 |
597 | fun setPubKey(key: String): Builder {
598 | this.pubKey = key
599 | return this
600 | }
601 |
602 | //原来缓存日期的意思是几天后从缓存目录移到日志目录 默认 0 即可
603 | //如果想让文件保留多少天 用 [setMaxAliveTime] 方法即可
604 | //大于 0 的时候 默认会放在缓存的位置上 [cachePath]
605 | fun setCacheDays(days: Int): Builder {
606 | if (days < 0) {
607 | this.cacheDays = 0
608 | } else {
609 | this.cacheDays = days
610 | }
611 | return this
612 | }
613 |
614 | fun setDebug(debug: Boolean): Builder {
615 | this.debug = debug
616 | return this
617 | }
618 |
619 | fun setLogLevel(level: LogLevel): Builder {
620 | this.logLevel = level
621 | return this
622 | }
623 |
624 | fun setConsoleLogOpen(consoleLogOpen: Boolean): Builder {
625 | this.consoleLogOpen = consoleLogOpen
626 | return this
627 | }
628 |
629 |
630 | fun setTag(logTag: String): Builder {
631 | tag = logTag
632 | return this
633 | }
634 |
635 |
636 | /**
637 | * [isOpen] true 设置每天一个日志文件
638 | * false 那么 [setMaxFileSize] 生效
639 | */
640 | fun setOneFileEveryday(isOpen: Boolean): Builder {
641 | this.oneFileEveryday = isOpen
642 | return this
643 | }
644 |
645 | fun setMaxFileSize(maxFileSize: Float): Builder {
646 | when {
647 | maxFileSize < 0 -> {
648 | this.maxFileSize = 0L
649 | }
650 | maxFileSize > 10 -> {
651 | this.maxFileSize = (10 * 1024 * 1024).toLong()
652 | }
653 | else -> {
654 | this.maxFileSize = (maxFileSize * 1024 * 1024).toLong()
655 | }
656 | }
657 | return this
658 | }
659 |
660 | /**
661 | * [day] 设置单个文件的过期时间 默认10天 在程序启动30S 以后会检查过期文件
662 | * 过期时间依据 当前系统时间 - 文件最后修改时间计算
663 | * 默认 单个文件保存 10天
664 | */
665 | fun setMaxAliveTime(day: Int): Builder {
666 | when {
667 | day < 0 -> {
668 | this.maxAliveTime = 0
669 | }
670 | day > 10 -> {
671 | this.maxAliveTime = 10
672 | }
673 | else -> {
674 | this.maxAliveTime = day
675 | }
676 | }
677 | return this
678 | }
679 |
680 | fun init() {
681 |
682 | if (!debug) {
683 | //判断如果是release 就强制使用 异步
684 | model = LogModel.Async
685 | //日志级别使用 Info
686 | logLevel = LogLevel.LEVEL_INFO
687 | }
688 |
689 | if (cachePath.isEmpty()) {
690 | //cachePath这个参数必传,而且要data下的私有文件目录,例如 /data/data/packagename/files/xlog, mmap文件会放在这个目录,如果传空串,可能会发生 SIGBUS 的crash。
691 | cachePath = defCachePath
692 | }
693 |
694 |
695 | android.util.Log.i(tag, "Xlog=========================================>")
696 | android.util.Log.i(
697 | tag,
698 | "info" + "\n"
699 | + "level:" + logLevel.level + "\n"
700 | + "model:" + model.model + "\n"
701 | + "cachePath:" + cachePath + "\n"
702 | + "logPath:" + logPath + "\n"
703 | + "namePreFix:" + namePreFix + "\n"
704 | + "cacheDays:" + cacheDays + "\n"
705 | + "pubKey:" + pubKey + "\n"
706 | + "consoleLogOpen:" + consoleLogOpen + "\n"
707 | + "maxFileSize:" + maxFileSize + "\n"
708 | )
709 |
710 | android.util.Log.i(tag, "Xlog=========================================<")
711 | Xlog.setConsoleLogOpen(consoleLogOpen)
712 | //每天一个日志文件
713 | if (oneFileEveryday) {
714 | Xlog.setMaxFileSize(0)
715 | } else {
716 | Xlog.setMaxFileSize(maxFileSize)
717 | }
718 |
719 | Xlog.setMaxAliveTime((maxAliveTime * 24 * 60 * 60).toLong())
720 |
721 | Xlog.appenderOpen(
722 | logLevel.level,
723 | model.model,
724 | cachePath,
725 | logPath,
726 | namePreFix,
727 | cacheDays,
728 | pubKey
729 | )
730 | Log.setLogImp(Xlog())
731 | }
732 |
733 |
734 | }
735 | ```
736 |
737 | # 下载
738 | Step 1. Add the JitPack repository to your build file
739 | Add it in your root build.gradle at the end of repositories:
740 | ```
741 | allprojects {
742 | repositories {
743 | ...
744 | maven { url 'https://www.jitpack.io' }
745 | }
746 | }
747 |
748 | ```
749 | Step 2. Add the dependency
750 | ```
751 | dependencies {
752 | implementation 'com.github.JiangHaiYang01:XLogHelper:Tag'
753 | }
754 | ```
755 |
756 | 添加 abiFilter
757 |
758 | ```
759 | android {
760 | compileSdkVersion 30
761 | buildToolsVersion "30.0.1"
762 |
763 | defaultConfig {
764 | ...
765 | ndk {
766 | abiFilter "armeabi-v7a"
767 | }
768 | }
769 |
770 | ...
771 | }
772 | ```
773 |
774 |
775 |
776 | > 当前最新版本
777 |
778 | [](https://www.jitpack.io/#JiangHaiYang01/XLogHelper)
779 |
780 |
781 | # 使用
782 |
783 | ## 初始化,建议放在 Application 中
784 |
785 | ```
786 | XLogHelper.create(this)
787 | .setModel(LogModel.Async)
788 | .setTag("TAG")
789 | .setConsoleLogOpen(true)
790 | .setLogLevel(LogLevel.LEVEL_INFO)
791 | .setNamePreFix("log")
792 | .setPubKey("572d1e2710ae5fbca54c76a382fdd44050b3a675cb2bf39feebe85ef63d947aff0fa4943f1112e8b6af34bebebbaefa1a0aae055d9259b89a1858f7cc9af9df1")
793 | .setMaxFileSize(1f)
794 | .setOneFileEveryday(true)
795 | .setCacheDays(0)
796 | .setMaxAliveTime(2)
797 | .init()
798 | ```
799 |
800 | 使用
801 | ```
802 | XLogHelper.i("======================> %s", i)
803 | XLogHelper.e("======================> %s", i)
804 | ```
805 |
806 | # 源码
807 |
808 | [GitHub](https://github.com/JiangHaiYang01/XLogHelper)
809 | [博客](https://allens.icu/posts/60625924/#more)
810 | [掘金](https://juejin.im/post/5f110c786fb9a07e802052cd)
811 |
812 |
813 |
814 |
815 | # 参考
816 |
817 |
818 | [Tencent/mars](https://github.com/Tencent/mars)
819 | [Mars Android 接口详细说明](https://github.com/Tencent/mars/wiki/Mars-Android-%E6%8E%A5%E5%8F%A3%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E)
820 | [【腾讯Bugly干货分享】微信mars 的高性能日志模块 xlog](https://zhuanlan.zhihu.com/p/23879436)
821 | [本地编译](https://github.com/Tencent/mars/wiki/Mars-Android-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97#local_compile)
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 30
7 | buildToolsVersion "30.0.1"
8 |
9 | defaultConfig {
10 | applicationId "com.allens.xloghelper"
11 | minSdkVersion 19
12 | targetSdkVersion 30
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 |
18 | ndk {
19 | abiFilter "armeabi-v7a"
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | }
31 |
32 | dependencies {
33 | implementation fileTree(dir: "libs", include: ["*.jar"])
34 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
35 | implementation 'androidx.core:core-ktx:1.3.0'
36 | implementation 'androidx.appcompat:appcompat:1.1.0'
37 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
38 | testImplementation 'junit:junit:4.12'
39 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
40 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
41 |
42 |
43 | implementation project(':xlog')
44 | // implementation 'com.github.JiangHaiYang01:XLogHelper:0.0.1'
45 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/allens/xloghelper/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xloghelper
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.allens.xloghelper", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/allens/xloghelper/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xloghelper
2 |
3 | import androidx.appcompat.app.AppCompatActivity
4 | import android.os.Bundle
5 | import com.allens.xlog.XLogHelper
6 | import kotlinx.android.synthetic.main.activity_main.*
7 |
8 | class MainActivity : AppCompatActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | setContentView(R.layout.activity_main)
12 |
13 |
14 | //添加日志
15 | add.setOnClickListener {
16 |
17 | for (i in 1..10) {
18 | XLogHelper.i("======================> %s", "阿斯蒂芬")
19 | }
20 | for (i in 1..10) {
21 | XLogHelper.e("======================> %s index %s", "fuck ", i)
22 | }
23 | for (i in 1..10) {
24 | XLogHelper.v("======================> %s", i)
25 | }
26 | }
27 |
28 |
29 | //close 之后不能写入
30 | close.setOnClickListener {
31 | XLogHelper.close()
32 | }
33 |
34 |
35 | //flush 之后 还能够再次写入
36 | flush.setOnClickListener {
37 | XLogHelper.flush(true)
38 | }
39 |
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/allens/xloghelper/MyApp.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xloghelper
2 |
3 | import android.app.Application
4 | import com.allens.xlog.LogLevel
5 | import com.allens.xlog.LogModel
6 | import com.allens.xlog.XLogHelper
7 |
8 | class MyApp :Application(){
9 |
10 | override fun onCreate() {
11 | super.onCreate()
12 | XLogHelper.create(this)
13 | .setModel(LogModel.Async)
14 | .setTag("TAG")
15 | .setDebug(true)
16 | .setConsoleLogOpen(true)
17 | .setLogLevel(LogLevel.LEVEL_INFO)
18 | .setNamePreFix("log")
19 | .setPubKey("572d1e2710ae5fbca54c76a382fdd44050b3a675cb2bf39feebe85ef63d947aff0fa4943f1112e8b6af34bebebbaefa1a0aae055d9259b89a1858f7cc9af9df1")
20 | .setMaxFileSize(1f)
21 | .setOneFileEveryday(true)
22 | .setCacheDays(0)
23 | .setMaxAliveTime(2)
24 | .init()
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
16 |
17 |
18 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | XLogHelper
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/test/java/com/allens/xloghelper/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xloghelper
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = "1.3.72"
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.0.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 | maven { url 'https://www.jitpack.io' }
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Jul 14 17:22:10 CST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':xlog'
2 | include ':app'
3 | rootProject.name = "XLogHelper"
--------------------------------------------------------------------------------
/xlog/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/xlog/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 |
5 | android {
6 | compileSdkVersion 30
7 | buildToolsVersion "30.0.1"
8 |
9 | defaultConfig {
10 | minSdkVersion 19
11 | targetSdkVersion 30
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | consumerProguardFiles "consumer-rules.pro"
17 |
18 | ndk {
19 | abiFilter "armeabi-v7a"
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | sourceSets {
31 | main {
32 | jniLibs {
33 | srcDir "libs"
34 | }
35 | }
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation fileTree(dir: "libs", include: ["*.jar"])
41 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
42 | implementation 'androidx.core:core-ktx:1.3.0'
43 | implementation 'androidx.appcompat:appcompat:1.1.0'
44 | testImplementation 'junit:junit:4.12'
45 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
46 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
47 |
48 | }
--------------------------------------------------------------------------------
/xlog/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/xlog/consumer-rules.pro
--------------------------------------------------------------------------------
/xlog/libs/armeabi-v7a/libc++_shared.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/xlog/libs/armeabi-v7a/libc++_shared.so
--------------------------------------------------------------------------------
/xlog/libs/armeabi-v7a/libmarsxlog.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JiangHaiYang01/XLogHelper/21f55ccc7fc4e9faca0a6321beaaeb41a42066f3/xlog/libs/armeabi-v7a/libmarsxlog.so
--------------------------------------------------------------------------------
/xlog/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/xlog/src/androidTest/java/com/allens/xlog/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.allens.xlog.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/xlog/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/allens/xlog/Builder.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | import android.content.Context
4 | import com.tencent.mars.xlog.Log
5 | import com.tencent.mars.xlog.Xlog
6 |
7 | class Builder(context: Context) {
8 |
9 | companion object {
10 | //日志的tag
11 | var tag = "log_tag"
12 | }
13 |
14 | //是否是debug 模式
15 | private var debug = true
16 |
17 |
18 | //是否打印控制台日志
19 | private var consoleLogOpen = true
20 |
21 |
22 | //是否每天一个日志文件
23 | private var oneFileEveryday = true
24 |
25 | //默认的位置
26 | private val defCachePath = context.getExternalFilesDir(null)?.path + "/mmap"
27 |
28 | // mmap 位置 默认缓存的位置
29 | private var cachePath = defCachePath
30 |
31 | //实际保存的log 位置
32 | private var logPath = context.getExternalFilesDir(null)?.path + "/logDir"
33 |
34 | //文件名称前缀 例如该值为TEST,生成的文件名为:TEST_20170102.xlog
35 | private var namePreFix = "log"
36 |
37 | //写入文件的模式
38 | private var model = LogModel.Async
39 |
40 | //最大文件大小
41 | //默认情况下,所有日志每天都写入一个文件。可以通过更改max_file_size将日志分割为多个文件。
42 | //单个日志文件的最大字节大小,默认为0,表示不分割
43 | //最大 当文件不能超过 10M
44 | private var maxFileSize = 0L
45 |
46 | //日志级别
47 | //debug 版本下建议把控制台日志打开,日志级别设为 Verbose 或者 Debug, release 版本建议把控制台日志关闭,日志级别使用 Info.
48 | private var logLevel = LogLevel.LEVEL_INFO
49 |
50 | //通过 python gen_key.py 获取到的公钥
51 | private var pubKey = ""
52 |
53 | //单个文件最大保留时间 最小 1天 默认时间 10天
54 | private var maxAliveTime = 10
55 |
56 | //缓存的天数 一般情况下填0即可。非0表示会在 _cachedir 目录下存放几天的日志。
57 | //原来缓存日期的意思是几天后从缓存目录移到日志目录
58 | private var cacheDays = 0
59 |
60 | fun setCachePath(cachePath: String): Builder {
61 | this.cachePath = cachePath
62 | return this
63 | }
64 |
65 | fun setLogPath(logPath: String): Builder {
66 | this.logPath = logPath
67 | return this
68 | }
69 |
70 |
71 | fun setNamePreFix(namePreFix: String): Builder {
72 | this.namePreFix = namePreFix
73 | return this
74 | }
75 |
76 | fun setModel(model: LogModel): Builder {
77 | this.model = model
78 | return this
79 | }
80 |
81 | fun setPubKey(key: String): Builder {
82 | this.pubKey = key
83 | return this
84 | }
85 |
86 | //原来缓存日期的意思是几天后从缓存目录移到日志目录 默认 0 即可
87 | //如果想让文件保留多少天 用 [setMaxAliveTime] 方法即可
88 | //大于 0 的时候 默认会放在缓存的位置上 [cachePath]
89 | fun setCacheDays(days: Int): Builder {
90 | if (days < 0) {
91 | this.cacheDays = 0
92 | } else {
93 | this.cacheDays = days
94 | }
95 | return this
96 | }
97 |
98 | //是否是debug 模式
99 | fun setDebug(debug: Boolean): Builder {
100 | this.debug = debug
101 | return this
102 | }
103 |
104 | //日志级别
105 | //debug 版本下建议把控制台日志打开,日志级别设为 Verbose 或者 Debug, release 版本建议把控制台日志关闭,日志级别使用 Info.
106 | fun setLogLevel(level: LogLevel): Builder {
107 | this.logLevel = level
108 | return this
109 | }
110 |
111 | //是否打印控制台日志
112 | fun setConsoleLogOpen(consoleLogOpen: Boolean): Builder {
113 | this.consoleLogOpen = consoleLogOpen
114 | return this
115 | }
116 |
117 |
118 | //设置日志打印的tag
119 | fun setTag(logTag: String): Builder {
120 | tag = logTag
121 | return this
122 | }
123 |
124 |
125 | /**
126 | * [isOpen] true 设置每天一个日志文件
127 | * false 那么 [setMaxFileSize] 生效
128 | */
129 | fun setOneFileEveryday(isOpen: Boolean): Builder {
130 | this.oneFileEveryday = isOpen
131 | return this
132 | }
133 |
134 | /**
135 | * [maxFileSize] 单位是M
136 | * 最大文件大小
137 | * 默认情况下,所有日志每天都写入一个文件。可以通过更改max_file_size将日志分割为多个文件。
138 | * 单个日志文件的最大字节大小,默认为0,表示不分割 最大 当文件不能超过 10M
139 | */
140 | fun setMaxFileSize(maxFileSize: Float): Builder {
141 | when {
142 | maxFileSize < 0 -> {
143 | this.maxFileSize = 0L
144 | }
145 | maxFileSize > 10 -> {
146 | this.maxFileSize = (10 * 1024 * 1024).toLong()
147 | }
148 | else -> {
149 | this.maxFileSize = (maxFileSize * 1024 * 1024).toLong()
150 | }
151 | }
152 | return this
153 | }
154 |
155 | /**
156 | * [day] 设置单个文件的过期时间 默认10天 在程序启动30S 以后会检查过期文件
157 | * 过期时间依据 当前系统时间 - 文件最后修改时间计算
158 | * 默认 单个文件保存 10天
159 | */
160 | fun setMaxAliveTime(day: Int): Builder {
161 | when {
162 | day < 0 -> {
163 | this.maxAliveTime = 0
164 | }
165 | day > 10 -> {
166 | this.maxAliveTime = 10
167 | }
168 | else -> {
169 | this.maxAliveTime = day
170 | }
171 | }
172 | return this
173 | }
174 |
175 | fun init() {
176 |
177 | if (!debug) {
178 | //判断如果是release 就强制使用 异步
179 | model = LogModel.Async
180 | //日志级别使用 Info
181 | logLevel = LogLevel.LEVEL_INFO
182 | }
183 |
184 | if (cachePath.isEmpty()) {
185 | //cachePath这个参数必传,而且要data下的私有文件目录,例如 /data/data/packagename/files/xlog, mmap文件会放在这个目录,如果传空串,可能会发生 SIGBUS 的crash。
186 | cachePath = defCachePath
187 | }
188 |
189 |
190 | android.util.Log.i(tag, "Xlog=========================================>")
191 | android.util.Log.i(
192 | tag,
193 | "日志配置信息\n\n"
194 | + "日志级别:" + logLevel + "\n"
195 |
196 | + "写入模式:" +
197 | if (model.model == LogModel.Sync.model) {
198 | "实时写入 不会加密"
199 | } else {
200 | "异步写入 会加密"
201 | } + "\n"
202 |
203 | + "mmap缓存位置:" + cachePath + "\n"
204 | + "实际保存的log位置:" + logPath + "\n"
205 | + "文件名称前缀:" + namePreFix + "\n"
206 | + "从缓存目录移到日志目录时间:" + cacheDays + "\n"
207 | + "公钥:" + pubKey + "\n"
208 | + "是否打印控制台日志:" + consoleLogOpen + "\n"
209 | + "单个日志文件最大:" +
210 | if (maxFileSize == 0L) {
211 | "一天一个"
212 | } else {
213 | "每个文件最大$maxFileSize"
214 | } + "\n"
215 | )
216 |
217 | android.util.Log.i(tag, "Xlog=========================================<")
218 | Xlog.setConsoleLogOpen(consoleLogOpen)
219 | //每天一个日志文件
220 | if (oneFileEveryday) {
221 | Xlog.setMaxFileSize(0)
222 | } else {
223 | Xlog.setMaxFileSize(maxFileSize)
224 | }
225 |
226 | Xlog.setMaxAliveTime((maxAliveTime * 24 * 60 * 60).toLong())
227 |
228 | Xlog.appenderOpen(
229 | logLevel.level,
230 | model.model,
231 | cachePath,
232 | logPath,
233 | namePreFix,
234 | cacheDays,
235 | pubKey
236 | )
237 | Log.setLogImp(Xlog())
238 | }
239 |
240 |
241 | }
--------------------------------------------------------------------------------
/xlog/src/main/java/com/allens/xlog/CustomXLog.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | import android.util.Log
4 | import com.tencent.mars.xlog.Xlog
5 |
6 | class CustomXLog : Xlog() {
7 |
8 | override fun logI(
9 | tag: String?,
10 | filename: String?,
11 | funcname: String?,
12 | line: Int,
13 | pid: Int,
14 | tid: Long,
15 | maintid: Long,
16 | log: String?
17 | ) {
18 | super.logI(tag, filename, funcname, line, pid, tid, maintid, log)
19 | Log.i(
20 | "allens",
21 | "tag:$tag \n" +
22 | "filename:$filename \n" +
23 | "funcname:$funcname \n" +
24 | "line:$line \n" +
25 | "pid:$pid \n" +
26 | "tid:$tid \n" +
27 | "maintid:$maintid \n" +
28 | "log:$log \n" +
29 | ""
30 | )
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/xlog/src/main/java/com/allens/xlog/LogLevel.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | enum class LogLevel(var level: Int) {
4 | LEVEL_ALL(0),
5 | LEVEL_VERBOSE(0),
6 | LEVEL_DEBUG(1),
7 | LEVEL_INFO(2),
8 | LEVEL_WARNING(3),
9 | LEVEL_ERROR(4),
10 | LEVEL_FATAL(5),
11 | LEVEL_NONE(6)
12 | }
--------------------------------------------------------------------------------
/xlog/src/main/java/com/allens/xlog/LogModel.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | enum class LogModel(var model: Int) {
4 | //异步写入 release 下建议 非实时写入
5 | Async(0),
6 |
7 | //同步写入 debug 下建议,实时写入 不会加密
8 | Sync(1)
9 | }
10 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/allens/xlog/XLogHelper.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | import android.content.Context
4 | import com.tencent.mars.xlog.Log
5 | import com.tencent.mars.xlog.Xlog
6 |
7 | object XLogHelper {
8 |
9 |
10 | init {
11 | System.loadLibrary("c++_shared")
12 | System.loadLibrary("marsxlog")
13 | }
14 |
15 |
16 | //创建
17 | fun create(context: Context): Builder {
18 | return Builder(context)
19 | }
20 |
21 | //=====================================================================================
22 | // v
23 | //=====================================================================================
24 | fun v(msg: String) {
25 | v(Builder.tag, msg)
26 | }
27 |
28 | fun v(format: String, vararg obj: Any?) {
29 | Log.v(Builder.tag, format, *obj)
30 | }
31 |
32 | //=====================================================================================
33 | // f
34 | //=====================================================================================
35 | fun f(msg: String) {
36 | f(Builder.tag, msg)
37 | }
38 |
39 | fun f(format: String, vararg obj: Any?) {
40 | Log.f(Builder.tag, format, *obj)
41 | }
42 |
43 |
44 | //=====================================================================================
45 | // e
46 | //=====================================================================================
47 | fun e(msg: String) {
48 | e(Builder.tag, msg)
49 | }
50 |
51 | fun e(format: String, vararg obj: Any?) {
52 | Log.e(Builder.tag, format, *obj)
53 | }
54 |
55 | //=====================================================================================
56 | // w
57 | //=====================================================================================
58 | fun w(msg: String) {
59 | w(Builder.tag, msg)
60 | }
61 |
62 | fun w(format: String, vararg obj: Any?) {
63 | Log.w(Builder.tag, format, *obj)
64 | }
65 |
66 | //=====================================================================================
67 | // w
68 | //=====================================================================================
69 | fun i(msg: String) {
70 | i(Builder.tag, msg)
71 | }
72 |
73 | fun i(format: String, vararg obj: Any?) {
74 | Log.i(Builder.tag, format, *obj)
75 | }
76 |
77 | //=====================================================================================
78 | // d
79 | //=====================================================================================
80 | fun d(msg: String) {
81 | d(Builder.tag, msg)
82 | }
83 |
84 | fun d(format: String, vararg obj: Any?) {
85 | Log.d(Builder.tag, format, *obj)
86 | }
87 |
88 | //关闭日志,不再写入
89 | fun close() {
90 | Log.appenderClose()
91 | }
92 |
93 | //当日志写入模式为异步时,调用该接口会把内存中的日志写入到文件。
94 | //isSync : true 为同步 flush,flush 结束后才会返回。
95 | //isSync : false 为异步 flush,不等待 flush 结束就返回。
96 | fun flush(isSync: Boolean) {
97 | Log.appenderFlush(isSync)
98 | }
99 |
100 |
101 | }
--------------------------------------------------------------------------------
/xlog/src/main/java/com/tencent/mars/xlog/Log.java:
--------------------------------------------------------------------------------
1 | package com.tencent.mars.xlog;
2 |
3 | import android.content.Context;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 | import android.os.Process;
7 | import android.widget.Toast;
8 |
9 | /**
10 | * @author zhaoyuan zhangweizang
11 | */
12 | public class Log {
13 | private static final String TAG = "mars.xlog.log";
14 |
15 | public static final int LEVEL_VERBOSE = 0;
16 | public static final int LEVEL_DEBUG = 1;
17 | public static final int LEVEL_INFO = 2;
18 | public static final int LEVEL_WARNING = 3;
19 | public static final int LEVEL_ERROR = 4;
20 | public static final int LEVEL_FATAL = 5;
21 | public static final int LEVEL_NONE = 6;
22 |
23 | // defaults to LEVEL_NONE
24 | private static int level = LEVEL_NONE;
25 | public static Context toastSupportContext = null;
26 |
27 | public interface LogImp {
28 |
29 | void logV(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
30 |
31 | void logI(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
32 |
33 | void logD(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
34 |
35 | void logW(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
36 |
37 | void logE(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
38 |
39 | void logF(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
40 |
41 | int getLogLevel();
42 |
43 | void appenderClose();
44 |
45 | void appenderFlush(boolean isSync);
46 |
47 | }
48 |
49 | private static LogImp debugLog = new LogImp() {
50 | private Handler handler = new Handler(Looper.getMainLooper());
51 |
52 | @Override
53 | public void logV(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
54 | if (level <= LEVEL_VERBOSE) {
55 | android.util.Log.v(tag, log);
56 | }
57 | }
58 |
59 | @Override
60 | public void logI(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
61 | if (level <= LEVEL_INFO) {
62 | android.util.Log.i(tag, log);
63 | }
64 | }
65 |
66 | @Override
67 | public void logD(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
68 | if (level <= LEVEL_DEBUG) {
69 | android.util.Log.d(tag, log);
70 | }
71 |
72 | }
73 |
74 | @Override
75 | public void logW(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
76 | if (level <= LEVEL_WARNING) {
77 | android.util.Log.w(tag, log);
78 | }
79 |
80 | }
81 |
82 | @Override
83 | public void logE(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
84 | if (level <= LEVEL_ERROR) {
85 | android.util.Log.e(tag, log);
86 | }
87 | }
88 |
89 | @Override
90 | public void logF(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, final String log) {
91 | if (level > LEVEL_FATAL) {
92 | return;
93 | }
94 | android.util.Log.e(tag, log);
95 |
96 | if (toastSupportContext != null) {
97 | handler.post(new Runnable() {
98 | @Override
99 | public void run() {
100 | Toast.makeText(toastSupportContext, log, Toast.LENGTH_LONG).show();
101 | }
102 | });
103 | }
104 | }
105 |
106 | @Override
107 | public int getLogLevel() {
108 | return level;
109 | }
110 |
111 | @Override
112 | public void appenderClose() {
113 |
114 | }
115 |
116 | @Override
117 | public void appenderFlush(boolean isSync) {
118 | }
119 |
120 | };
121 |
122 | private static LogImp logImp = debugLog;
123 |
124 | public static void setLogImp(LogImp imp) {
125 | logImp = imp;
126 | }
127 |
128 | public static LogImp getImpl() {
129 | return logImp;
130 | }
131 |
132 | public static void appenderClose() {
133 | if (logImp != null) {
134 | logImp.appenderClose();
135 | }
136 | }
137 |
138 | public static void appenderFlush(boolean isSync) {
139 | if (logImp != null) {
140 | logImp.appenderFlush(isSync);
141 | }
142 | }
143 |
144 | public static int getLogLevel() {
145 | if (logImp != null) {
146 | return logImp.getLogLevel();
147 | }
148 | return LEVEL_NONE;
149 | }
150 |
151 | public static void setLevel(final int level, final boolean jni) {
152 | Log.level = level;
153 | android.util.Log.w(TAG, "new log level: " + level);
154 |
155 | if (jni) {
156 | Xlog.setLogLevel(level);
157 | //android.util.Log.e(TAG, "no jni log level support");
158 | }
159 | }
160 |
161 | /**
162 | * use f(tag, format, obj) instead
163 | *
164 | * @param tag
165 | * @param msg
166 | */
167 | public static void f(final String tag, final String msg) {
168 | f(tag, msg, (Object[]) null);
169 | }
170 |
171 | /**
172 | * use e(tag, format, obj) instead
173 | *
174 | * @param tag
175 | * @param msg
176 | */
177 | public static void e(final String tag, final String msg) {
178 | e(tag, msg, (Object[]) null);
179 | }
180 |
181 | /**
182 | * use w(tag, format, obj) instead
183 | *
184 | * @param tag
185 | * @param msg
186 | */
187 | public static void w(final String tag, final String msg) {
188 | w(tag, msg, (Object[]) null);
189 | }
190 |
191 | /**
192 | * use i(tag, format, obj) instead
193 | *
194 | * @param tag
195 | * @param msg
196 | */
197 | public static void i(final String tag, final String msg) {
198 | i(tag, msg, (Object[]) null);
199 | }
200 |
201 | /**
202 | * use d(tag, format, obj) instead
203 | *
204 | * @param tag
205 | * @param msg
206 | */
207 | public static void d(final String tag, final String msg) {
208 | d(tag, msg, (Object[]) null);
209 | }
210 |
211 | /**
212 | * use v(tag, format, obj) instead
213 | *
214 | * @param tag
215 | * @param msg
216 | */
217 | public static void v(final String tag, final String msg) {
218 | v(tag, msg, (Object[]) null);
219 | }
220 |
221 | public static void f(String tag, final String format, final Object... obj) {
222 | if (logImp != null) {
223 | final String log = obj == null ? format : String.format(format, obj);
224 | logImp.logF(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
225 | }
226 | }
227 |
228 | public static void e(String tag, final String format, final Object... obj) {
229 | if (logImp != null) {
230 | String log = obj == null ? format : String.format(format, obj);
231 | if (log == null) {
232 | log = "";
233 | }
234 | logImp.logE(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
235 | }
236 | }
237 |
238 | public static void w(String tag, final String format, final Object... obj) {
239 | if (logImp != null) {
240 | String log = obj == null ? format : String.format(format, obj);
241 | if (log == null) {
242 | log = "";
243 | }
244 | logImp.logW(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
245 | }
246 | }
247 |
248 | public static void i(String tag, final String format, final Object... obj) {
249 | if (logImp != null) {
250 | String log = obj == null ? format : String.format(format, obj);
251 | if (log == null) {
252 | log = "";
253 | }
254 | logImp.logI(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
255 | }
256 | }
257 |
258 | public static void d(String tag, final String format, final Object... obj) {
259 | if (logImp != null) {
260 | String log = obj == null ? format : String.format(format, obj);
261 | if (log == null) {
262 | log = "";
263 | }
264 | logImp.logD(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
265 | }
266 | }
267 |
268 | public static void v(String tag, final String format, final Object... obj) {
269 | if (logImp != null) {
270 | String log = obj == null ? format : String.format(format, obj);
271 | if (log == null) {
272 | log = "";
273 | }
274 | logImp.logV(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
275 | }
276 | }
277 |
278 | public static void printErrStackTrace(String tag, Throwable tr, final String format, final Object... obj) {
279 | if (logImp != null) {
280 | String log = obj == null ? format : String.format(format, obj);
281 | if (log == null) {
282 | log = "";
283 | }
284 | log += " " + android.util.Log.getStackTraceString(tr);
285 | logImp.logE(tag, "", "", 0, Process.myPid(), Thread.currentThread().getId(), Looper.getMainLooper().getThread().getId(), log);
286 | }
287 | }
288 |
289 | private static final String SYS_INFO;
290 |
291 | static {
292 | final StringBuilder sb = new StringBuilder();
293 | try {
294 | sb.append("VERSION.RELEASE:[" + android.os.Build.VERSION.RELEASE);
295 | sb.append("] VERSION.CODENAME:[" + android.os.Build.VERSION.CODENAME);
296 | sb.append("] VERSION.INCREMENTAL:[" + android.os.Build.VERSION.INCREMENTAL);
297 | sb.append("] BOARD:[" + android.os.Build.BOARD);
298 | sb.append("] DEVICE:[" + android.os.Build.DEVICE);
299 | sb.append("] DISPLAY:[" + android.os.Build.DISPLAY);
300 | sb.append("] FINGERPRINT:[" + android.os.Build.FINGERPRINT);
301 | sb.append("] HOST:[" + android.os.Build.HOST);
302 | sb.append("] MANUFACTURER:[" + android.os.Build.MANUFACTURER);
303 | sb.append("] MODEL:[" + android.os.Build.MODEL);
304 | sb.append("] PRODUCT:[" + android.os.Build.PRODUCT);
305 | sb.append("] TAGS:[" + android.os.Build.TAGS);
306 | sb.append("] TYPE:[" + android.os.Build.TYPE);
307 | sb.append("] USER:[" + android.os.Build.USER + "]");
308 | } catch (Throwable e) {
309 | e.printStackTrace();
310 | }
311 |
312 | SYS_INFO = sb.toString();
313 | }
314 |
315 | public static String getSysInfo() {
316 | return SYS_INFO;
317 | }
318 | }
319 |
--------------------------------------------------------------------------------
/xlog/src/main/java/com/tencent/mars/xlog/Xlog.java:
--------------------------------------------------------------------------------
1 | package com.tencent.mars.xlog;
2 |
3 | public class Xlog implements Log.LogImp {
4 |
5 | public static final int LEVEL_ALL = 0;
6 | public static final int LEVEL_VERBOSE = 0;
7 | public static final int LEVEL_DEBUG = 1;
8 | public static final int LEVEL_INFO = 2;
9 | public static final int LEVEL_WARNING = 3;
10 | public static final int LEVEL_ERROR = 4;
11 | public static final int LEVEL_FATAL = 5;
12 | public static final int LEVEL_NONE = 6;
13 |
14 | public static final int AppednerModeAsync = 0;
15 | public static final int AppednerModeSync = 1;
16 |
17 | static class XLoggerInfo {
18 | public int level;
19 | public String tag;
20 | public String filename;
21 | public String funcname;
22 | public int line;
23 | public long pid;
24 | public long tid;
25 | public long maintid;
26 | }
27 |
28 | public static void open(boolean isLoadLib, int level, int mode, String cacheDir, String logDir, String nameprefix, String pubkey) {
29 | if (isLoadLib) {
30 | System.loadLibrary("c++_shared");
31 | System.loadLibrary("marsxlog");
32 | }
33 |
34 | appenderOpen(level, mode, cacheDir, logDir, nameprefix, 0, pubkey);
35 | }
36 |
37 | private static String decryptTag(String tag) {
38 | return tag;
39 | }
40 |
41 | @Override
42 | public void logV(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
43 | logWrite2(LEVEL_VERBOSE, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
44 | }
45 |
46 | @Override
47 | public void logD(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
48 | logWrite2(LEVEL_DEBUG, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
49 | }
50 |
51 | @Override
52 | public void logI(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
53 | logWrite2(LEVEL_INFO, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
54 | }
55 |
56 | @Override
57 | public void logW(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
58 | logWrite2(LEVEL_WARNING, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
59 | }
60 |
61 | @Override
62 | public void logE(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
63 | logWrite2(LEVEL_ERROR, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
64 | }
65 |
66 | @Override
67 | public void logF(String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log) {
68 | logWrite2(LEVEL_FATAL, decryptTag(tag), filename, funcname, line, pid, tid, maintid, log);
69 | }
70 |
71 |
72 | public static native void logWrite(XLoggerInfo logInfo, String log);
73 |
74 | public static native void logWrite2(int level, String tag, String filename, String funcname, int line, int pid, long tid, long maintid, String log);
75 |
76 | @Override
77 | public native int getLogLevel();
78 |
79 | public static native void setLogLevel(int logLevel);
80 |
81 | public static native void setAppenderMode(int mode);
82 |
83 | public static native void setConsoleLogOpen(boolean isOpen); //set whether the console prints log
84 |
85 |
86 | // public static native void setErrLogOpen(boolean isOpen); //set whether the prints err log into a separate file
87 |
88 | public static native void appenderOpen(int level, int mode, String cacheDir, String logDir, String nameprefix, int cacheDays, String pubkey);
89 |
90 | public static native void setMaxFileSize(long size);
91 |
92 | /**
93 | * should be called before appenderOpen to take effect
94 | * @param duration alive seconds
95 | */
96 | public static native void setMaxAliveTime(long duration);
97 |
98 | @Override
99 | public native void appenderClose();
100 |
101 | @Override
102 | public native void appenderFlush(boolean isSync);
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/xlog/src/test/java/com/allens/xlog/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.allens.xlog
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------