Android进阶:一、日志打印和保存策略
前言:
項目開始沒有做好日志統計工作,每次有問題后端都得找前端對接,嚴重影響工作效率。最近特地在項目中加上日志保存策略,在此分享,供需要的人學習。
一.更詳細的日志信息
既然決定自定義一個log,那我們就可以讓它顯示更多的信息,如線程信息:threadId,threadName等:
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,有時候不知道到底是前端出了問題還是后端的問題,為了更好更快速的定位,后端應該知道前端的日志保存在哪里。這就需要我們制定一個日志保存策略。(即使要上傳日志,也應該先保存成文件再上傳文件,不然每一條日志調用一次接口,接口的壓力會很大,很不合理)
由于保存日志的過程是個耗時過程,我們需要開啟線程去保存。但是日志產生的頻率可能很高,又不能采用一般的線程去處理,太多的線程也會損耗性能。所以我們應該考慮隊列的形式保存日志,然后一條一條的去保存。
我們使用HandlerThread來處理這個任務。HandlerThread是一個可以使用handler的Thread。當我們把消息保存到消息隊列中去之后會在線程中去處理,又能保證不會產生很多線程。其實這里也可以使用instentservice實現,這個服務適合量大而不太耗時的任務。
最后在一個方法中統一打印和保存即可:
自定義的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;}} }以上就是全部內容,希望對大家有所幫助,需要交流技術的可以私信
轉載于:https://blog.51cto.com/14295695/2384021
總結
以上是生活随笔為你收集整理的Android进阶:一、日志打印和保存策略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PLSQL Developer连接ora
- 下一篇: golang 开源代理