HrLogUtil 低成本邮件发送日志 和 快速埋点 功能
HrLogUtil
HrLogUtil 是一个 Android 平台的日志记录工具库。主要解决低成本邮件发送日志和快速埋点功能
https://github.com/shaoweijie/hr-log 急需Github上的star,多谢各位
基础功能特性
- 埋点功能:支持自动埋点记录用户交互行为。(视图级别,弹窗必须采用ASM方式)
- 日志上传:支持将日志通过邮件发送。
- 日志级别支持:支持 VERBOSE、INFO、DEBUG、WARNING、ERROR 等日志级别。
- 日志输出方式:支持控制台输出和文件输出。
- 日志文件管理:支持日志文件的自动写入、压缩和邮件发送。
- 崩溃捕获:自动捕获未处理的异常并记录日志。
- 生命周期日志:记录 Activity 的生命周期事件。
使用方法
依赖
应用级别的build.gradle中添加:
android{//...// 排除sum mail中的META-INF文件packagingOptions{jniLibs{excludes+=['META-INF/*']}resources{excludes+=['project.properties','META-INF/*']}}}dependencies{//...implementation'com.sun.mail:android-mail:1.6.7'implementation'com.sun.mail:android-activation:1.6.7'implementation'com.gitee.Hancher:hrlog:1.0.2'}初始化
在 Application 的 onCreate 中初始化日志记录功能:
- 日志文件大小限制:
MAX_FILE_SIZE定义单个日志文件的最大大小。 - 最大文件数量:
MAX_FILE_COUNT定义保留的日志文件最大数量。 - 邮件配置:通过
SEND_MAIL,SEND_SMTP,SEND_PSW配置邮件发送参数。
HrLogUtil.IS_WRITE_TO_FILE=true;//默认写入文件HrLogUtil.MAX_FILE_COUNT=10;//默认保留10个日志文件HrLogUtil.MAX_FILE_SIZE=50*1024*1024;//默认单个日志文件最大50MHrLogUtil.initWriteToFile(context,"your_email@example.com","smtp.example.com","password");日志记录
支持多种日志级别:
HrLogUtil.d();HrLogUtil.v("Verbose message");HrLogUtil.i("Info message");HrLogUtil.d("Debug message");HrLogUtil.w("Warning message");HrLogUtil.e("Error message");日志如下:
2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin D (MainActivity.java:16) onCreate(): 2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin V (MainActivity.java:17) onCreate(): Verbose message 2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin I (MainActivity.java:18) onCreate(): Info message 2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin D (MainActivity.java:19) onCreate(): Debug message 2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin W (MainActivity.java:20) onCreate(): Warning message 2026-08-06 16:38:22.749 11235-11235 HrLogUtil com.hancher.plugin E (MainActivity.java:21) onCreate(): Error message折叠后:
16:40:30.231 D (MainActivity.java:16) onCreate(): 16:40:30.231 V (MainActivity.java:17) onCreate(): Verbose message 16:40:30.231 I (MainActivity.java:18) onCreate(): Info message 16:40:30.231 D (MainActivity.java:19) onCreate(): Debug message 16:40:30.231 W (MainActivity.java:20) onCreate(): Warning message 16:40:30.231 E (MainActivity.java:21) onCreate(): Error message崩溃捕获
无需配置,自动捕获未处理的异常:
折叠前:
2026-08-06 17:07:36.296 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): Test 2026-08-06 17:07:36.306 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): com.hancher.plugins.MainActivity.lambda$onCreate$0(MainActivity.java:28) 2026-08-06 17:07:36.314 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): com.hancher.plugins.MainActivity$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0) 2026-08-06 17:07:36.323 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): android.os.Handler.handleCallback(Handler.java:942) 2026-08-06 17:07:36.328 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): android.os.Handler.dispatchMessage(Handler.java:99) 2026-08-06 17:07:36.333 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): android.os.Looper.loopOnce(Looper.java:240) 2026-08-06 17:07:36.337 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): android.os.Looper.loop(Looper.java:351) 2026-08-06 17:07:36.340 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): android.app.ActivityThread.main(ActivityThread.java:8422) 2026-08-06 17:07:36.343 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): java.lang.reflect.Method.invoke(Native Method) 2026-08-06 17:07:36.346 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584) 2026-08-06 17:07:36.349 17194-17194 HrLogUtil com.hancher.plugin E 全埋点(异常): com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)折叠后:
16:51:16.620 E 全埋点(异常): Test 16:51:16.629 E 全埋点(异常): com.hancher.plugins.MainActivity.lambda$onCreate$0(MainActivity.java:28) 16:51:16.636 E 全埋点(异常): com.hancher.plugins.MainActivity$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0) 16:51:16.645 E 全埋点(异常): android.os.Handler.handleCallback(Handler.java:942) 16:51:16.650 E 全埋点(异常): android.os.Handler.dispatchMessage(Handler.java:99) 16:51:16.655 E 全埋点(异常): android.os.Looper.loopOnce(Looper.java:240) 16:51:16.660 E 全埋点(异常): android.os.Looper.loop(Looper.java:351) 16:51:16.664 E 全埋点(异常): android.app.ActivityThread.main(ActivityThread.java:8422) 16:51:16.668 E 全埋点(异常): java.lang.reflect.Method.invoke(Native Method) 16:51:16.671 E 全埋点(异常): com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584) 16:51:16.675 E 全埋点(异常): com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)生命周期日志
无需配置,自动记录 Activity 生命周期:
2026-08-06 16:49:45.178 13768-13768 HrLogUtil com.hancher.plugin D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onCreate 2026-08-06 16:49:45.403 13768-13768 HrLogUtil com.hancher.plugin D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onStart 2026-08-06 16:49:45.413 13768-13768 HrLogUtil com.hancher.plugin I 全埋点(生命周期): com.hancher.plugins.MainActivity -- onResume 2026-08-06 16:49:45.478 13768-13768 HrLogUtil com.hancher.plugin D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onPause 2026-08-06 16:49:45.485 13768-13768 HrLogUtil com.hancher.plugin D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onStop 2026-08-06 16:49:45.491 13768-13768 HrLogUtil com.hancher.plugin D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onSaveInstanceState折叠后:
16:49:45.178 D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onCreate 16:49:45.403 D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onStart 16:49:45.413 I 全埋点(生命周期): com.hancher.plugins.MainActivity -- onResume 16:49:45.478 D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onPause 16:49:45.485 D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onStop 16:49:45.491 D 全埋点(生命周期): com.hancher.plugins.MainActivity -- onSaveInstanceState埋点功能
无需配置,自动记录 View 的点击事件(无法记录弹窗中的点击事件,可以不用此方案,直接采用ASM方案):
findViewById(R.id.main).setOnClickListener(view->{HrLogUtil.d("回调");});折叠前:
2026-08-06 16:58:04.541 14901-14901 HrLogUtil com.hancher.plugin V 全埋点(点击):{"name":"LinearLayout","id":"R.id.main"} 2026-08-06 16:58:04.542 14901-14901 HrLogUtil com.hancher.plugin D (MainActivity.java:18) lambda$onCreate$0(): 回调折叠后:
16:58:04.541 V 全埋点(点击):{"name":"LinearLayout","id":"R.id.main"} 16:58:04.542 D (MainActivity.java:18) lambda$onCreate$0(): 回调日志上传
上传日志文件:
HrLogUtil.uploadLog(activity,"邮件标题","邮件正文内容");ASM埋点
gradle8.9、asm9.6
gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zipproject build.gradle
buildscript { dependencies { classpath 'com.gitee.Hancher:hr-asm9-log:1.0.2' } }module build.gradle
plugins { alias(libs.plugins.android.application) id 'com.hancher.plugins.hrasm9log' } android { compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } } dependencies { // 字节码 implementation 'org.ow2.asm:asm:9.6' implementation 'org.ow2.asm:asm-commons:9.6' implementation 'org.ow2.asm:asm-analysis:9.6' implementation 'org.ow2.asm:asm-tree:9.6' implementation 'org.ow2.asm:asm-util:9.6' implementation 'com.android.tools.build:gradle:8.4.2', { exclude group:'org.ow2.asm' } }覆写 LogUtil
com/hancher/plugins/hrasm9log/LogUtil
packagecom.hancher.plugins.hrasm9log;importandroid.content.res.Resources;importandroid.util.Log;importandroid.view.View;importandroid.widget.TextView;publicclassHrAsmLogUtil{publicstaticvoidclick(Viewv){if(v==null){Log.d("HrLogUtil","全埋点(点击):v is null");return;}StringBufferstringBuffer=newStringBuffer();Stringname=v.getClass().getName();try{name=name.substring(name.lastIndexOf('.')+1);}catch(Exceptione){}stringBuffer.append("类名:").append(name);if(vinstanceofTextView){TextViewb=(TextView)v;stringBuffer.append(", 文本框内容:").append(b.getText().toString());}intid=v.getId();if(id!=View.NO_ID){finalResourcesr=v.getResources();if(id>0/*&& Resources.resourceHasPackage(id)*/&&r!=null){try{stringBuffer.append(", 视图:").append("R."+r.getResourceTypeName(id)+"."+r.getResourceEntryName(id));}catch(Resources.NotFoundExceptione){}}}Log.d("HrLogUtil","全埋点(点击):"+stringBuffer.toString());}}效果
2025-08-12 10:10:59.703 22432-22432 HrLogUtil com.hancher.hrasm9demo D 全埋点(点击):类名:FloatingActionButton, 视图:R.id.fab 2025-08-12 10:11:03.361 22432-22432 HrLogUtil com.hancher.hrasm9demo D 全埋点(点击):类名:MaterialButton, 文本框内容:Next, 视图:R.id.button_first 2025-08-12 10:11:05.968 22432-22432 HrLogUtil com.hancher.hrasm9demo D 全埋点(点击):类名:MaterialButton, 文本框内容:Previous, 视图:R.id.button_secondgradle5.6.4、asm7.0
gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zipproject build.gradle
dependencies { classpath 'com.android.tools.build:gradle:3.6.3' classpath "com.gitee.Hancher:hr-asm-log:1.0.0" }module build.gradle
apply plugin: 'com.hancher.plugins.hrasmlog' android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } registerTransform(new com.hancher.plugins.hrasmlog.HrAsmLogPluginProject()) } dependencies { // 字节码 implementation 'org.ow2.asm:asm:7.0' implementation 'org.ow2.asm:asm-commons:7.0' implementation 'org.ow2.asm:asm-tree:7.0' implementation 'org.ow2.asm:asm-util:7.0' implementation 'com.gitee.Hancher:hr-asm-log:1.0.0', { exclude group:'com.android.tools' exclude group:'com.android.tools.build' } }覆写 LogUtil
com/hancher/plugins/hrasmlog/LogUtil
packagecom.hancher.plugins.hrasmlog;importandroid.content.res.Resources;importandroid.util.Log;importandroid.view.View;importandroid.widget.TextView;publicclassLogUtil{publicstaticvoidclick(Viewv){if(v==null){Log.d("LogUtil","全埋点(点击):v is null");return;}StringBufferstringBuffer=newStringBuffer();Stringname=v.getClass().getName();try{name=name.substring(name.lastIndexOf('.')+1);}catch(Exceptione){}stringBuffer.append("类名:").append(name);if(vinstanceofTextView){TextViewb=(TextView)v;stringBuffer.append(", 文本框内容:").append(b.getText().toString());}intid=v.getId();if(id!=View.NO_ID){finalResourcesr=v.getResources();if(id>0/*&& Resources.resourceHasPackage(id)*/&&r!=null){try{stringBuffer.append(", 视图:").append("R."+r.getResourceTypeName(id)+"."+r.getResourceEntryName(id));}catch(Resources.NotFoundExceptione){}}}Log.d("LogUtil","全埋点(点击):"+stringBuffer.toString());}}效果
2025-08-08 15:23:32.548 4030-4030/com.hancher.asmdemo D/LogUtil: 全埋点(点击):类名:FloatingActionButton, 视图:R.id.fab 2025-08-08 15:23:43.210 4030-4030/com.hancher.asmdemo D/LogUtil: 全埋点(点击):类名:FloatingActionButton, 视图:R.id.fab2 2025-08-08 15:23:44.725 4030-4030/com.hancher.asmdemo D/LogUtil: 全埋点(点击):类名:AppCompatButton, 文本框内容:Next, 视图:R.id.button_first 2025-08-08 15:23:49.300 4030-4030/com.hancher.asmdemo D/LogUtil: 全埋点(点击):类名:AppCompatButton, 文本框内容:Previous, 视图:R.id.button_second 2025-08-08 15:23:50.486 4030-4030/com.hancher.asmdemo D/LogUtil: 全埋点(点击):类名:AppCompatButton, 文本框内容:Next, 视图:R.id.button_first许可证
本项目采用 MIT 许可证。详情请查看 LICENSE 文件。
