Android Log的多場景使用
阿新 • • 發佈:2019-01-08
背景:不同情況下,我們有不同的需求,下面我們總結一下所有的case採用不同的策略。
方案一:
通過一個開關,決定是否輸出log,也可以決定是否輸出到檔案,方便我們除錯使用。
package ls.utils; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import com.config.AppConfig; /** * Title: LogUtils.java Description: 日誌工具類:開發過程中,日誌輸出 * * @author song * @date 2014-9-9 下午1:22:55 * @version V1.0 */ public class LogUtils { /** * isWrite:用於開關是否吧日誌寫入txt檔案中</p> */ private static final boolean isWrite = false; /** * isDebug :是用來控制,是否列印日誌 */ private static final boolean isDeBug = true; /** * 存放日誌檔案的所在路徑 */ private static final String DIRPATH = AppConfig.LOG_DIRPATH; // private static final String DIRPATH = "/log"; /** * 存放日誌的文字名 */ private static final String LOGNAME = AppConfig.LOG_FILENAME; // private static final String LOGNAME = "log.txt"; /** * 設定時間的格式 */ private static final String INFORMAT = "yyyy-MM-dd HH:mm:ss"; /** * VERBOSE日誌形式的識別符號 */ public static final int VERBOSE = 5; /** * DEBUG日誌形式的識別符號 */ public static final int DEBUG = 4; /** * INFO日誌形式的識別符號 */ public static final int INFO = 3; /** * WARN日誌形式的識別符號 */ public static final int WARN = 2; /** * ERROR日誌形式的識別符號 */ public static final int ERROR = 1; /** * 把異常用來輸出日誌的綜合方法 * * @param @param tag 日誌標識 * @param @param throwable 丟擲的異常 * @param @param type 日誌型別 * @return void 返回型別 * @throws */ public static void log(String tag, Throwable throwable, int type) { log(tag, exToString(throwable), type); } /** * 用來輸出日誌的綜合方法(文字內容) * * @param @param tag 日誌標識 * @param @param msg 要輸出的內容 * @param @param type 日誌型別 * @return void 返回型別 * @throws */ public static void log(String tag, String msg, int type) { switch (type) { case VERBOSE: v(tag, msg);// verbose等級 break; case DEBUG: d(tag, msg);// debug等級 break; case INFO: i(tag, msg);// info等級 break; case WARN: w(tag, msg);// warn等級 break; case ERROR: e(tag, msg);// error等級 break; default: break; } } /** * verbose等級的日誌輸出 * * @param tag * 日誌標識 * @param msg * 要輸出的內容 * @return void 返回型別 * @throws */ public static void v(String tag, String msg) { // 是否開啟日誌輸出 if (isDeBug) { Log.v(tag, msg); } // 是否將日誌寫入檔案 if (isWrite) { write(tag, msg); } } /** * debug等級的日誌輸出 * * @param tag * 標識 * @param msg * 內容 * @return void 返回型別 * @throws */ public static void d(String tag, String msg) { if (isDeBug) { Log.d(tag, msg); } if (isWrite) { write(tag, msg); } } /** * info等級的日誌輸出 * * @param tag 標識 * @param msg 內容 * @return void 返回型別 * @throws */ public static void i(String tag, String msg) { if (isDeBug) { Log.i(tag, msg); } if (isWrite) { write(tag, msg); } } /** * warn等級的日誌輸出 * * @param tag 標識 * @param msg 內容 * @return void 返回型別 * @throws */ public static void w(String tag, String msg) { if (isDeBug) { Log.w(tag, msg); } if (isWrite) { write(tag, msg); } } /** * error等級的日誌輸出 * * @param tag 標識 * @param msg 內容 * @return void 返回型別 */ public static void e(String tag, String msg) { if (isDeBug) { Log.w(tag, msg); } if (isWrite) { write(tag, msg); } } /** * 用於把日誌內容寫入制定的檔案 * * @param @param tag 標識 * @param @param msg 要輸出的內容 * @return void 返回型別 * @throws */ public static void write(String tag, String msg) { String path = FileUtils.createMkdirsAndFiles(DIRPATH, LOGNAME); if (TextUtils.isEmpty(path)) { return; } String log = DateFormat.format(INFORMAT, System.currentTimeMillis()) + tag + "========>>" + msg + "\n=================================分割線================================="; FileUtils.write2File(path, log, true); } /** * 用於把日誌內容寫入制定的檔案 * * @param tag * 標籤 * @param ex * 異常 */ public static void write(Throwable ex) { write("", exToString(ex)); } /** * 把異常資訊轉化為字串 * * @param ex 異常資訊 * @return 異常資訊字串 */ private static String exToString(Throwable ex) { Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); printWriter.close(); String result = writer.toString(); return result; } }
方案二:
方案一雖然在release情況無法輸出了,但是對於發不出去的包,通過反編譯還是可以看到log的,可以推出來我們的一些邏輯,這對於我們是不需要的
DEBUG通過一個final的boolean值控制,編譯的時候,會自動去掉。
if (DEBUG) { Log.e(TAG, "err", e); } 方案三: 方案二雖然可以實現,但是還是比較複雜的。每次需要寫三行,我們可以通過另一種方式實現這種策略(混淆配置)在混淆檔案中:
-assumenosideeffects class android.util.Log { public static *** v(...); public static *** d(...); public static *** i(...); public static *** w(...); public static *** e(...); }
但是不可以配置:-dontoptimize
方案四:
這是一種系統開發常用的方法,我們的應用釋出出去了,這時我們發現了問題,但是沒有log怎麼除錯呢?這是就出現了方案四,我們可以動態配置在當前手機是否開啟log
直接參照系統原始碼吧:
這裡面有個知識點:
android.util.Log.isLoggable(TAG, level);
這個開關時可以動態配置的,正常沒有配置的時候,是返回false的。
當設定了
adb shell setprop log.tag.TAG VERBOSE
之後,就會返回true了/packages/services/Telephony/src/com/android/services/telephony/Log.java /* * Copyright 2014, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.services.telephony; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.IllegalFormatException; import java.util.Locale; /** * Manages logging for the entire module. */ final public class Log { // Generic tag for all In Call logging private static final String TAG = "Telephony"; public static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */ public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG); public static final boolean INFO = isLoggable(android.util.Log.INFO); public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE); public static final boolean WARN = isLoggable(android.util.Log.WARN); public static final boolean ERROR = isLoggable(android.util.Log.ERROR); private Log() {} public static boolean isLoggable(int level) { return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level); } public static void d(String prefix, String format, Object... args) { if (DEBUG) { android.util.Log.d(TAG, buildMessage(prefix, format, args)); } } public static void d(Object objectPrefix, String format, Object... args) { if (DEBUG) { android.util.Log.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args)); } } public static void i(String prefix, String format, Object... args) { if (INFO) { android.util.Log.i(TAG, buildMessage(prefix, format, args)); } } public static void i(Object objectPrefix, String format, Object... args) { if (INFO) { android.util.Log.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args)); } } public static void v(String prefix, String format, Object... args) { if (VERBOSE) { android.util.Log.v(TAG, buildMessage(prefix, format, args)); } } public static void v(Object objectPrefix, String format, Object... args) { if (VERBOSE) { android.util.Log.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args)); } } public static void w(String prefix, String format, Object... args) { if (WARN) { android.util.Log.w(TAG, buildMessage(prefix, format, args)); } } public static void w(Object objectPrefix, String format, Object... args) { if (WARN) { android.util.Log.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args)); } } public static void e(String prefix, Throwable tr, String format, Object... args) { if (ERROR) { android.util.Log.e(TAG, buildMessage(prefix, format, args), tr); } } public static void e(Object objectPrefix, Throwable tr, String format, Object... args) { if (ERROR) { android.util.Log.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args), tr); } } public static void wtf(String prefix, Throwable tr, String format, Object... args) { android.util.Log.wtf(TAG, buildMessage(prefix, format, args), tr); } public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) { android.util.Log.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args), tr); } public static void wtf(String prefix, String format, Object... args) { String msg = buildMessage(prefix, format, args); android.util.Log.wtf(TAG, msg, new IllegalStateException(msg)); } public static void wtf(Object objectPrefix, String format, Object... args) { String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args); android.util.Log.wtf(TAG, msg, new IllegalStateException(msg)); } /** * Redact personally identifiable information for production users. * If we are running in verbose mode, return the original string, otherwise * return a SHA-1 hash of the input string. */ public static String pii(Object pii) { if (pii == null || VERBOSE) { return String.valueOf(pii); } return "[" + secureHash(String.valueOf(pii).getBytes()) + "]"; } private static String secureHash(byte[] input) { MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { return null; } messageDigest.update(input); byte[] result = messageDigest.digest(); return encodeHex(result); } private static String encodeHex(byte[] bytes) { StringBuffer hex = new StringBuffer(bytes.length * 2); for (int i = 0; i < bytes.length; i++) { int byteIntValue = bytes[i] & 0xff; if (byteIntValue < 0x10) { hex.append("0"); } hex.append(Integer.toString(byteIntValue, 16)); } return hex.toString(); } private static String getPrefixFromObject(Object obj) { return obj == null ? "<null>" : obj.getClass().getSimpleName(); } private static String buildMessage(String prefix, String format, Object... args) { String msg; try { msg = (args == null || args.length == 0) ? format : String.format(Locale.US, format, args); } catch (IllegalFormatException ife) { wtf("Log", ife, "IllegalFormatException: formatString='%s' numArgs=%d", format, args.length); msg = format + " (An error occurred while formatting the message.)"; } return String.format(Locale.US, "%s: %s", prefix, msg); } }
以上的每個知識點都可以用於很多用途,不只是在log這裡!
參考連結:
http://blog.csdn.net/vk5176891/article/details/46537625