当前位置: 首页 > news >正文

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

http://www.jsqmd.com/news/1351410/

相关文章:

  • 超绝落地窗:从心动到实现,全面解析系统窗的工程实践
  • Clawdbot部署实战:Kimi、MiniMax、GLM三大AI模型API配置详解
  • 2026年中国赴科特迪瓦办理签证怎么选才稳妥?正通广迈 - 热点品牌推荐
  • RUSLE 2.0水土流失模拟技术与GIS应用实践
  • pdf免费转换用哪个好?盘点免费PDF转Word图片网页端电脑端无水印推荐 - 办公小帮手
  • 地铁车辆柔性轮对轴箱建模与刚柔耦合分析实践
  • 高通X105 5G-Advanced模组实战(1): Linux驱动移植与USB枚举的踩坑经验
  • 江苏的连续变化镜片哪家好? - 中媒介
  • 智慧渔业海上养殖数字孪生实践方向与难点拆解分析
  • 时间序列数据分析:孤岛与间隙问题解决方案
  • AI Agent技能膨胀陷阱:从性能衰退到架构优化的实战解析
  • 毕业设计全流程指南:从选题到答辩的实战技巧
  • 彻底解决VC++运行库缺失与版本冲突:从原理到实战修复指南
  • 2026 年 8月上海房产纠纷律所挑选参考:孙青律师业务特点盘点 + 全流程维权指南 - 孙青律师13681945561
  • 从OCR到文档智能:多模态大模型如何实现结构与矢量化的统一理解
  • 唐山管道疏通马桶/下水道/地漏除臭就近师傅 24 小时随叫随到上门维修(2026新) - 北京优选
  • linux ftrace 原理剖析:内核函数追踪是怎么实现的
  • Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
  • 基于Django的智能教育系统开发实践
  • 惠州瓷砖空鼓检测修复维修_2026珠三角东岸瓷砖空鼓维修避坑指南与电话 - 雨婺虹修缮
  • 赛尔号电王觉醒前瞻:从技能机制到实战配置的深度解析
  • 终端家居五金提升幸福感品牌 - 中媒介
  • 企业从0到1搭建增长系统的实操方法与落地路径
  • Ubuntu 22.04/24.04 C/C++开发环境搭建:从GCC到VS Code全流程指南
  • 想改汽车音响有没有门店推荐 2026十大口碑工作室实力测评 价格透明避坑指南 - 工业品牌热点
  • mysql添加指定权限
  • SECS/GEM300标准须实现哪些功能?
  • Redis内存管理与淘汰策略实战指南
  • 正规的线上超市闪电仓怎么选?郑州前店后仓超市加盟模式解析 - 优质品牌商家
  • AI Agent安全加固与生态扩展:从沙箱隔离到工具包集成的工程实践