package cn.qqtheme.framework.util; import android.os.Debug; import android.os.Environment; import android.util.Log; import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; import cn.qqtheme.framework.AppConfig; /** * 将信息记录到控制台的LogCat,显示调用方法及所在的文件、行号,方便开发时调试查错。 * 注意:在Debug状态下开启,在Release状态下关闭,敏感信息不宜打印,否则被非法之徒抓取贻害无穷。 * * @author 李玉江[QQ:1023694760] * @since 2013/11/2 */ public final class LogUtils { private static final int MIN_STACK_OFFSET = 3;// starts at this class after two native calls private static final int MAX_STACK_TRACE_SIZE = 131071; //128 KB - 1 private static final int METHOD_COUNT = 2; // show method count in trace private static boolean isDebug = AppConfig.DEBUG_ENABLE;// 是否调试模式 private static String debugTag = AppConfig.DEBUG_TAG;// LogCat的标记 public static void setIsDebug(boolean isDebug) { LogUtils.isDebug = isDebug; } public static boolean isDebug() { return isDebug; } public static void setDebugTag(String debugTag) { LogUtils.debugTag = debugTag; } public static String getDebugTag() { return debugTag; } /** * Verbose. * * @param message the message */ public static void verbose(String message) { verbose("", message); } /** * Verbose. * * @param object the object * @param message the message */ public static void verbose(Object object, String message) { verbose(object.getClass().getSimpleName(), message); } /** * 记录“verbose”级别的信息 * * @param tag the tag * @param msg the msg */ public static void verbose(String tag, String msg) { if (isDebug) { tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag; msg = msg + getTraceElement(); Log.v(tag, msg); } } /** * Debug. * * @param message the message */ public static void debug(String message) { debug("", message); } /** * Debug. * * @param object the object * @param message the message */ public static void debug(Object object, String message) { debug(object.getClass().getSimpleName(), message); } /** * 记录“debug”级别的信息 * * @param tag the tag * @param msg the msg */ public static void debug(String tag, String msg) { if (isDebug) { tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag; msg = msg + getTraceElement(); Log.d(tag, msg); } } /** * Warn. * * @param e the e */ public static void warn(Throwable e) { warn(toStackTraceString(e)); } /** * Warn. * * @param message the message */ public static void warn(String message) { warn("", message); } /** * Warn. * * @param object the object * @param message the message */ public static void warn(Object object, String message) { warn(object.getClass().getSimpleName(), message); } /** * Warn. * * @param object the object * @param e the e */ public static void warn(Object object, Throwable e) { warn(object.getClass().getSimpleName(), toStackTraceString(e)); } /** * 记录“warn”级别的信息 * * @param tag the tag * @param msg the msg */ public static void warn(String tag, String msg) { if (isDebug) { tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag; msg = msg + getTraceElement(); Log.w(tag, msg); } } /** * Error. * * @param e the e */ public static void error(Throwable e) { error(toStackTraceString(e)); } /** * Error. * * @param message the message */ public static void error(String message) { error("", message); } /** * Error. * * @param object the object * @param message the message */ public static void error(Object object, String message) { error(object.getClass().getSimpleName(), message); } /** * Error. * * @param object the object * @param e the e */ public static void error(Object object, Throwable e) { error(object.getClass().getSimpleName(), toStackTraceString(e)); } /** * 记录“error”级别的信息 * * @param tag the tag * @param msg the msg */ public static void error(String tag, String msg) { if (isDebug) { tag = debugTag + ((tag == null || tag.trim().length() == 0) ? "" : "-") + tag; msg = msg + getTraceElement(); Log.e(tag, msg); } } /** * 在某个方法中调用生成.trace文件。然后拿到电脑上用DDMS工具打开分析 * * @see #stopMethodTracing() */ public static void startMethodTracing() { if (isDebug) { Debug.startMethodTracing(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + debugTag + ".trace"); } } /** * Stop method tracing. */ public static void stopMethodTracing() { if (isDebug) { Debug.stopMethodTracing(); } } /** * To stack trace string string. * <p> * 此方法参见:https://github.com/Ereza/CustomActivityOnCrash * * @param throwable the throwable * @return the string */ public static String toStackTraceString(Throwable throwable) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); throwable.printStackTrace(pw); String stackTraceString = sw.toString(); //Reduce data to 128KB so we don't get a TransactionTooLargeException when sending the intent. //The limit is 1MB on Android but some devices seem to have it lower. //See: http://developer.android.com/reference/android/os/TransactionTooLargeException.html //And: http://stackoverflow.com/questions/11451393/what-to-do-on-transactiontoolargeexception#comment46697371_12809171 if (stackTraceString.length() > MAX_STACK_TRACE_SIZE) { String disclaimer = " [stack trace too large]"; stackTraceString = stackTraceString.substring(0, MAX_STACK_TRACE_SIZE - disclaimer.length()) + disclaimer; } pw.close(); return stackTraceString; } /** * 可显示调用方法所在的文件行号,在AndroidStudio的logcat处可点击定位。 * 此方法参考:https://github.com/orhanobut/logger */ private static String getTraceElement() { try { int methodCount = METHOD_COUNT; StackTraceElement[] trace = Thread.currentThread().getStackTrace(); int stackOffset = _getStackOffset(trace); //corresponding method count with the current stack may exceeds the stack trace. Trims the count if (methodCount + stackOffset > trace.length) { methodCount = trace.length - stackOffset - 1; } String level = " "; StringBuilder builder = new StringBuilder(); for (int i = methodCount; i > 0; i--) { int stackIndex = i + stackOffset; if (stackIndex >= trace.length) { continue; } builder.append("\n") .append(level) .append(_getSimpleClassName(trace[stackIndex].getClassName())) .append(".") .append(trace[stackIndex].getMethodName()) .append(" ") .append("(") .append(trace[stackIndex].getFileName()) .append(":") .append(trace[stackIndex].getLineNumber()) .append(")"); level += " "; } return builder.toString(); } catch (Exception e) { Log.w(debugTag, e); return ""; } } /** * Determines the starting index of the stack trace, after method calls made by this class. * * @param trace the stack trace * @return the stack offset */ private static int _getStackOffset(StackTraceElement[] trace) { for (int i = MIN_STACK_OFFSET; i < trace.length; i++) { StackTraceElement e = trace[i]; String name = e.getClassName(); if (!name.equals(LogUtils.class.getName())) { return --i; } } return -1; } private static String _getSimpleClassName(String name) { int lastIndex = name.lastIndexOf("."); return name.substring(lastIndex + 1); } }