三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

HrLogUtil 低成本邮件发送日志 和 快速埋点 功能

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.zip

project 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_second

gradle5.6.4、asm7.0

gradle-wrapper.properties

distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip

project 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 文件。

← 返回列表