Android logcat日志封裝
logcat痛點
在Android開發中使用logcat非常頻繁,logcat能幫我們定位問題,但是在日常使用中發現每次使用都需要傳遞tag,并且會遇到輸出頻率很高的log,在多人混合開發的場景下,不過濾很難找到自己打的log,調試別人代碼時看不懂他人輸出的log。這些問題會影響到整個開發效率,并且無法對整個項目的log進行統一管理。
目的
為了方便管理和規范log輸出,增加logcat使用的便捷性封裝logcat需要滿足以下幾點:
1、動態管理日志輸出
2、使用便捷,只需關注需要輸出的massage即可
3、日志信息顯示齊全,方便定位與統一
日志輸出開關
通過廣播及系統屬性控制日志的開關,通過注冊LOG_SWITCH = "android.App.all.log" 廣播,用來控制所有app日志開關,方便多app統一管理,另外通過注冊包名后綴+".log"廣播(如包名為:com.yxd.logtest,則action為:logtest.log),來控制當前app的日志開關,同時還將開關值存入到系統屬性中,以免退出后需重新打開。
通過adb模擬發送廣播,實現開關控制,adb命令:adb shell am broadcast -a android.app.all.log --ei status 1
public static void init(Context context) { if (sContext != null){ return; } IntentFilter intentFilter = new IntentFilter(); //系統屬性名稱不可超過32字符,所以使用包名最后一段+.log 來做屬性名 String[] strings = context.getPackageName().split("\."); intentFilter.addAction(strings[strings.length - 1] + ".log"); intentFilter.addAction(LOG_SWITCH); context.registerReceiver(new LogSwitchReceiver(), intentFilter); String allLog = SystemProperties.get(LOG_SWITCH); String thisLog = SystemProperties.get(strings[strings.length - 1] + ".log"); if ((!thisLog.isEmpty() && "1".equals(thisLog)) || (!allLog.isEmpty() && "1".equals(allLog))) { HQLog.SWITCH = true; } sContext = context; }
static class LogSwitchReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(LOG_STATUS, 0); Log.d("LogUtils", "LogSwitchReceiver action: " + intent.getAction() + " status: " + status); SystemProperties.set(intent.getAction(), String.valueOf(status)); HQLog.SWITCH = status == 1; } }
如果為debug版本,則默認打開日志輸出
public static boolean SWITCH = BuildConfig.DEBUG;
輸出格式
為了方便使用及定位,輸出時需要將類名及行數顯示,且在logcat上點擊該行可以直接定位到對應輸出代碼,類似crash報錯日志,點擊藍色字體即可跳轉至輸出行
具體實現如下:
public static void e(String message) { if (SWITCH) { Log.e(getTag(), message); } } private static String getTag() { StackTraceElement[] traceElements = Thread.currentThread().getStackTrace(); String tag = null; if (traceElements != null && traceElements.length > 4) { StackTraceElement traceElement = traceElements[4]; tag = "(" + traceElement.getFileName() + ":" + traceElement.getLineNumber() + ")" + traceElement.getMethodName(); } else { tag = "ULog"; } return tag;
完整代碼
使用時需在application里面init一下
public class MyApplication extends Application { private static Context mContext; @Override public void onCreate() { super.onCreate(); mContext = this; LogUtils.init(this); AppInfo.initContext(this); } public static Context getApplication(){ return mContext; } }
public class LogUtils { public static String LOG_SWITCH = "android.app.all.log"; //log開關狀態 0關 1開 public static String LOG_STATUS = "status"; @SuppressLint("StaticFieldLeak") private static Context sContext; private static LogSwitchReceiver sLogSwitchReceiver = new LogSwitchReceiver(); public static void init(Context context) { if (sContext != null){ return; } IntentFilter intentFilter = new IntentFilter(); //系統屬性名稱不可超過32字符,所以使用包名最后一段+.log 來做屬性名 String[] strings = context.getPackageName().split("\."); intentFilter.addAction(strings[strings.length - 1] + ".log"); intentFilter.addAction(LOG_SWITCH); context.registerReceiver(new LogSwitchReceiver(), intentFilter); String allLog = SystemProperties.get(LOG_SWITCH); String thisLog = SystemProperties.get(strings[strings.length - 1] + ".log"); if ((!thisLog.isEmpty() && "1".equals(thisLog)) || (!allLog.isEmpty() && "1".equals(allLog))) { ULog.SWITCH = true; } sContext = context; } public static void release() { if (sContext != null && sLogSwitchReceiver != null) { sContext.unregisterReceiver(sLogSwitchReceiver); } } static class LogSwitchReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra(LOG_STATUS, 0); Log.d("LogUtils", "LogSwitchReceiver action: " + intent.getAction() + " status: " + status); SystemProperties.set(intent.getAction(), String.valueOf(status)); ULog.SWITCH = status == 1; } } }
public class ULog { public static boolean SWITCH = BuildConfig.DEBUG; public static void v(String message) { if (SWITCH) { Log.v(getTag(), message); } } public static void d(String message) { if (SWITCH) { Log.d(getTag(), message); } } public static void i(String message) { if (SWITCH) { Log.i(getTag(), message); } } public static void w(String message) { if (SWITCH) { Log.w(getTag(), message); } } public static void e(String message) { if (SWITCH) { Log.e(getTag(), message); } } public static void getStackTraceString(Throwable tr) { if (SWITCH) { Log.getStackTraceString(tr); } } private static String getTag() { StackTraceElement[] traceElements = Thread.currentThread().getStackTrace(); String tag = null; if (traceElements != null && traceElements.length > 4) { StackTraceElement traceElement = traceElements[4]; tag = "(" + traceElement.getFileName() + ":" + traceElement.getLineNumber() + ")" + traceElement.getMethodName(); } else { tag = "ULog"; } return tag; } private static String generateLogcatText(String msg, int place, String message) { return generateLogcatText(Thread.currentThread().getStackTrace(), msg, place, message); } private static String generateLogcatText(StackTraceElement[] traceElements, String msg, int place, String message) { try { StringBuilder taskName = new StringBuilder(); if (traceElements != null && traceElements.length > place) { StackTraceElement traceElement = traceElements[place]; taskName.append(traceElement.getMethodName()).append("() : ").append(message); } return taskName.toString(); } catch (Throwable throwable) { return msg; } } }