Android进阶:一、日志打印和保存策略

编程入门 行业动态 更新时间:2024-10-22 12:26:55

Android<a href=https://www.elefans.com/category/jswz/34/1769503.html style=进阶:一、日志打印和保存策略"/>

Android进阶:一、日志打印和保存策略

前言:

项目开始没有做好日志统计工作,每次有问题后端都得找前端对接,严重影响工作效率。最近特地在项目中加上日志保存策略,在此分享,供需要的人学习。

更详细的日志信息

既然决定自定义一个log,那我们就可以让它显示更多的信息,如线程信息:threadId,threadName等:

private String getFunctionName() {StackTraceElement[] sts = Thread.currentThread().getStackTrace();if (sts == null) {return null;}for (StackTraceElement st : sts) {if (st.isNativeMethod()) {continue;}if (st.getClassName().equals(Thread.class.getName())) {continue;}if (st.getClassName().equals(this.getClass().getName())) {continue;}Thread t = Thread.currentThread();return "[T(id:" + t.getId() +", name:" + t.getName() +", priority:" + t.getPriority() +", groupName:" + t.getThreadGroup().getName() +"): " + st.getFileName() + ":"+ st.getLineNumber() + " " + st.getMethodName() + " ]";}return "";}

StackTrace(堆栈轨迹)存放的就是方法调用栈的信息,我们从中获取方法执行的线程相关的信息,以及执行的方法名称等。这些信息能帮助我们更好的查找问题之所在。

private void logPrint(int logLevel, Object msg) {if (isDebug) {String name = getFunctionName();customTag = TextUtils.isEmpty(customTag) ? defaultTag : customTag;Log.println(logLevel, customTag, name + " - " + msg);}}

使用Log.println方法打印相关信息即可。

日志保存策略

后端的人在测试的时候会遇到BUG,有时候不知道到底是前端出了问题还是后端的问题,为了更好更快速的定位,后端应该知道前端的日志保存在哪里。这就需要我们制定一个日志保存策略。(即使要上传日志,也应该先保存成文件再上传文件,不然每一条日志调用一次接口,接口的压力会很大,很不合理)

由于保存日志的过程是个耗时过程,我们需要开启线程去保存。但是日志产生的频率可能很高,又不能采用一般的线程去处理,太多的线程也会损耗性能。所以我们应该考虑队列的形式保存日志,然后一条一条的去保存。

public void initSaveStrategy(Context context) {if (saveLogStrategy != null || !isDebug) {return;}final int MAX_BYTES = 1024 * 1024;String diskPath = Environment.getExternalStorageDirectory().getAbsolutePath();File cacheFile = context.getCacheDir();if (cacheFile != null) {diskPath = cacheFile.getAbsolutePath();}String folder = diskPath + File.separatorChar + "log";HandlerThread ht = new HandlerThread("SohuLiveLogger." + folder);ht.start();Handler handler = new SaveLogStrategy.WriteHandler(ht.getLooper(), folder, MAX_BYTES);saveLogStrategy = new SaveLogStrategy(handler);}
 public static class WriteHandler extends Handler {private final String folder;private final int maxFileSize;WriteHandler(@NonNull Looper looper, @NonNull String folder, int maxFileSize) {super(checkNotNull(looper));this.folder = checkNotNull(folder);this.maxFileSize = maxFileSize;}@Overridepublic void handleMessage(@NonNull Message msg) {String content = (String) msg.obj;FileWriter fileWriter = null;File logFile = getLogFile(folder, "logs");try {fileWriter = new FileWriter(logFile, true);writeLog(fileWriter, content);fileWriter.flush();fileWriter.close();} catch (IOException e) {if (fileWriter != null) {try {fileWriter.flush();fileWriter.close();} catch (IOException e1) {}}}}

我们使用HandlerThread来处理这个任务。HandlerThread是一个可以使用handler的Thread。当我们把消息保存到消息队列中去之后会在线程中去处理,又能保证不会产生很多线程。其实这里也可以使用instentservice实现,这个服务适合量大而不太耗时的任务。

最后在一个方法中统一打印和保存即可:

    private void logPrint(int logLevel, Object msg) {if (isDebug) {String name = getFunctionName();if (saveLogStrategy != null) {saveLogStrategy.log(Log.ERROR, customTag, name + " - " + msg);}Log.println(logLevel, customTag, name + " - " + msg);}}

自定义的log策略还是比较简单,主要就是这个思想:打印日志信息详细,保存要采用队列的形式。一下是全部代码:

public class Logger {public final static String tag = "";private static SaveLogStrategy saveLogStrategy;private final static boolean logFlag = true;private static Logger logger;private int logLevel = Log.VERBOSE;private static boolean isDebug = BuildConfig.DEBUG;private String customTag = null;private Logger(String customTag) {this.customTag = customTag;}public void initSaveStrategy(Context context) {if (saveLogStrategy != null || !isDebug) {return;}final int MAX_BYTES = 1024 * 1024;String diskPath = Environment.getExternalStorageDirectory().getAbsolutePath();File cacheFile = context.getCacheDir();if (cacheFile != null) {diskPath = cacheFile.getAbsolutePath();}String folder = diskPath + File.separatorChar + "log";HandlerThread ht = new HandlerThread("Logger." + folder);ht.start();Handler handler = new SaveLogStrategy.WriteHandler(ht.getLooper(), folder, MAX_BYTES);saveLogStrategy = new SaveLogStrategy(handler);}public static Logger getLogger(String tag) {if (logger == null) {logger = new Logger(tag);}return logger;}public static Logger getLogger() {if (logger == null) {logger = new Logger(tag);}return logger;}/*** Verbose(2) 级别日志** @param str String*/public void v(Object str) {logLevel = Log.VERBOSE;logPrint(logLevel, str);}/*** Debug(3) 级别日志** @param str String*/public void d(Object str) {logLevel = Log.DEBUG;logPrint(logLevel, str);}/*** Info(4) 级别日志** @param str String*/public void i(Object str) {logLevel = Log.INFO;logPrint(logLevel, str);}/*** Warn(5) 级别日志** @param str String*/public void w(Object str) {logLevel = Log.WARN;logPrint(logLevel, str);}/*** Error(6) 级别日志** @param str String*/public void e(Object str) {logLevel = Log.ERROR;logPrint(logLevel, str);}private void logPrint(int logLevel, Object msg) {if (isDebug) {String name = getFunctionName();if (saveLogStrategy != null) {saveLogStrategy.log(Log.ERROR, customTag, name + " - " + msg);}Log.println(logLevel, customTag, name + " - " + msg);}}/*** 获取当前方法名** @return 方法名*/private String getFunctionName() {StackTraceElement[] sts = Thread.currentThread().getStackTrace();if (sts == null) {return null;}for (StackTraceElement st : sts) {if (st.isNativeMethod()) {continue;}if (st.getClassName().equals(Thread.class.getName())) {continue;}if (st.getClassName().equals(this.getClass().getName())) {continue;}Thread t = Thread.currentThread();return "[Thread(id:" + t.getId() +", name:" + t.getName() +", priority:" + t.getPriority() +", groupName:" + t.getThreadGroup().getName() +"): " + st.getFileName() + ":"+ st.getLineNumber() + " " + st.getMethodName() + " ]";}return "";}
}
public class SaveLogStrategy {@NonNullprivate final Handler handler;public SaveLogStrategy(@NonNull Handler handler) {this.handler = checkNotNull(handler);}public void log(int level, @Nullable String tag, @NonNull String message) {checkNotNull(message);handler.sendMessage(handler.obtainMessage(level, message));}static class WriteHandler extends Handler {private final String folder;private final int maxFileSize;WriteHandler(@NonNull Looper looper, @NonNull String folder, int maxFileSize) {super(checkNotNull(looper));this.folder = checkNotNull(folder);this.maxFileSize = maxFileSize;}@SuppressWarnings("checkstyle:emptyblock")@Overridepublic void handleMessage(@NonNull Message msg) {String content = (String) msg.obj;FileWriter fileWriter = null;File logFile = getLogFile(folder, "logs");try {fileWriter = new FileWriter(logFile, true);writeLog(fileWriter, content);fileWriter.flush();fileWriter.close();} catch (IOException e) {if (fileWriter != null) {try {fileWriter.flush();fileWriter.close();} catch (IOException e1) {}}}}private void writeLog(@NonNull FileWriter fileWriter, @NonNull String content) throws IOException {checkNotNull(fileWriter);checkNotNull(content);fileWriter.append("\n").append(content);}private File getLogFile(@NonNull String folderName, @NonNull String fileName) {checkNotNull(folderName);checkNotNull(fileName);File folder = new File(folderName);if (!folder.exists()) {if (!folder.mkdirs()) {Log.println(Log.ERROR, "saveLog", "文件未创建成功,可能是读写权限没给");}}int newFileCount = 0;File newFile;File existingFile = null;newFile = new File(folder, String.format("%s_%s.txt", fileName, newFileCount));while (newFile.exists()) {existingFile = newFile;newFileCount++;newFile = new File(folder, String.format("%s_%s.txt", fileName, newFileCount));}if (existingFile != null) {if (existingFile.length() >= maxFileSize) {return newFile;}return existingFile;}return newFile;}}
}

以上就是全部内容,希望对大家有所帮助

更多推荐

Android进阶:一、日志打印和保存策略

本文发布于:2023-07-28 16:06:41,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1244995.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:进阶   策略   日志   Android

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!