Android 封装Log工具并上传Log文件到服务器(带类名、方法名、行数、Crash的捕捉)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

writeToFile(CRASH_LEVEL, logData);

}

public void e(String logData) {

writeToFile(ERROR_LEVEL, logData);

}

public void w(String logData) {

writeToFile(WARNING_LEVEL, logData);

}

public void i(String logData) {

writeToFile(INFO_LEVEL, type, logData);

}

public void d(String logData) {

writeToFile(DEBUG_LEVEL, logData);

}

witeTofile就是写入到文件的方法:

private static StackTraceElement getCallerStackTraceElement() {

return Thread.currentThread().getStackTrace()[5];

}

/**

  • 将Log信息写入文件

  • isDouble为是否连续两次写入,防止连续两次上传服务器。

  • @param level

  • @param type

  • @param logData

*/

public static void writeToFile(String level, String logData) {

if (null == logPath) {

LogUtil.e(TAG, “logPath == null ,未初始化LogToFile”);

return;

}

String fileName = logPath + “/AppLogs_Android.log”;

StackTraceElement caller = getCallerStackTraceElement();

// 获取到类名

String callerClazzName = caller.getClassName();

callerClazzName = callerClazzName.substring(callerClazzName

.lastIndexOf(“.”) + 1);

//要写入的LOG内容

String log = type + " - " + callerClazzName + " - " + caller.getMethodName() + " - line " + caller.getLineNumber() + " - " + logData + “\n”;

LogUtil.d(TAG, log);

//如果父路径不存在

File file = new File(logPath);

if (!file.exists()) {

file.mkdirs();//创建父路径

}

FileOutputStream fos = null;

BufferedWriter bw = null;

try {

fos = new FileOutputStream(fileName, true);

bw = new BufferedWriter(new OutputStreamWriter(fos));

bw.write(log);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (bw != null) {

bw.close();//关闭缓冲流

}

} catch (IOException e) {

e.printStackTrace();

}

}

if (getLogsFileSize(fileName) >= 1f) {

sendToServer(fileName);

} else if (level == CRASH_LEVEL || level == ERROR_LEVEL || level == WARNING_LEVEL) {

sendToServer(fileName);

}

}

上面有一个巨顶的方法:

Thread.currentThread().getStackTrace()[5]

这行函数能够得离当前执行代码的指令。(注:不一定是5)

因为在执行命令时,指令栈保存当前线程最近执行的代码。

它的原理是这样的:

1、MainActivity : LogTool.d(xxx,xxx)

跳转-》

2、LogTool: d(xxx,xx){wirteToFile(xxx,xxx)}

跳转-》

3、LogTool:writeToFile(xxx){}

假如上面是一个指令栈,那么我就去获取这个栈的栈底元素 [1] MainActivity… 这一行,得到的是个StackTraceElement 对象。

得到该对象之后,我们可以将该指令反射,通过getClassName()获取类名,通过getMethodName()获取方法名、通过getLineNumber()获取当前行数,就省的我们一步一步去找具体哪行了。

所以上面代码中的第5行只是我这边的情况,别的代码就要自己去推理具体在哪一行咯。

接下来就是上传到服务器,这里使用Okhttp,上传的格式是包括文件+一些附带String信息,所以使用mutipart来提交表单,并且开启一个线程来提交。

/**

  • 上传Log文件至服务器

  • @return

*/

public static void sendToServer(final String pathName) {

new Thread(new Runnable() {

@Override

public void run() {

final File file = new File(pathName);

MediaType MEDIA_TYPE_TXT = MediaType.parse(“text/plain”);

RequestBody fileBody = MultipartBody.create(MEDIA_TYPE_TXT, file);

MultipartBody multiBuilder = new MultipartBody.Builder()

.setType(MultipartBody.FORM)

.addFormDataPart(“log_file”, file.getName(), fileBody)

.addFormDataPart(“system”, “Android”)

.addFormDataPart(“phone_number”, “110”).build();

Request request = new Request.Builder().url(apiUrl).post(multiBuilder).build();

OkHttpClient okHttpClient = new OkHttpClient();

Call call = okHttpClient.newCall(request);

call.enqueue(new Callback() {

@Override

public void onFailure(Call call, IOException e) {

//请求失败的处理

LogUtil.e(TAG, "上传Log文件失败 : " + e.toString());

if (getLogsFileSize(pathName) > 1f) {

file.delete();

}

}

@Override

public void onResponse(Call call, Response response) throws IOException {

//请求成功的处理

LogUtil.d(TAG, "上传Log文件成功 : " + response.body().string());

//清空文件

file.delete();

}

});

}

}).start();

}

到这里就上传完啦。

但是我们还没有关注Crash的捕捉,因为我们自己无法判断Crash是出现在什么地方的,所以我们要写一个全局的Crash捕捉器,而Android提供了这个类,叫:Thread.UncaughtExceptionHandler,我们通过实现该接口,重写uncaughtException()方法,来处理遇到异常的情况。

该类如下(也需要在App初始化时init):

public class CrashHandlerManager implements Thread.UncaughtExceptionHandler {

private static final String TAG = “CrashHandlerManager”;

private static CrashHandlerManager instance;

private Context context;

private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;

//收集信息集合

private Map<String, String> infoMap = new HashMap<>();

public synchronized static CrashHandlerManager getInstance() {

if (instance == null) {

instance = new CrashHandlerManager();

}

return instance;

}

private CrashHandlerManager() {

}

/**

  • 初始化程序异常处理器

  • @param context

*/

public void initCrash(Context context) {

this.context = context;

//获取系统默认的UncaughtException处理器

uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();

//设置该CrashHandler为程序得默认处理器

Thread.setDefaultUncaughtExceptionHandler(this);

}

/**

  • 当UncaughtException发生会进入此方法

  • @param t

  • @param e

*/

@Override

public void uncaughtException(Thread t, Throwable e) {

if (!handleException(e) && uncaughtExceptionHandler != null) {

//如果自定义没有处理就交给系统去处理

uncaughtExceptionHandler.uncaughtException(t, e);

}

}

尾声

以薪资待遇为基础,以发展为最终目标,要在高薪资的地方,谋求最好的发展!

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

uncaughtExceptionHandler.uncaughtException(t, e);

}

}

尾声

以薪资待遇为基础,以发展为最终目标,要在高薪资的地方,谋求最好的发展!

下面是有几位Android行业大佬对应上方技术点整理的一些进阶资料。

[外链图片转存中…(img-5SxrKMxx-1713674020237)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-NyUQNQFF-1713674020237)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值