如何在Android中启用/禁用日志级别?
我有很多的日志语句来debugging例如。
Log.v(TAG, "Message here"); Log.w(TAG, " WARNING HERE");
同时在设备电话上部署此应用程序,我想closures从我可以启用/禁用日志的详细日志logging。
一个常见的方法是创build一个名为loglevel的int,并根据loglevel定义其debugging级别。
public static int LOGLEVEL = 2; public static boolean ERROR = LOGLEVEL > 0; public static boolean WARN = LOGLEVEL > 1; ... public static boolean VERBOSE = LOGLEVEL > 4; if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown if (WARN) Log.w(TAG, "WARNING HERE"); // Still goes through
稍后,您可以更改所有debugging输出级别的LOGLEVEL。
Android文档说明以下关于日志级别 :
除了在开发过程中,绝不应该将Verbose编译成应用程序。 debugging日志被编译,但在运行时剥离。 错误,警告和信息日志始终保持不变。
因此,您可能需要考虑剥离日志详细日志logging语句, 可能使用另一个答案中build议的ProGuard 。
根据文档,您可以使用系统属性在开发设备上configuration日志logging。 要设置的属性是log.tag.<YourTag>
,它应该设置为以下值之一: VERBOSE
, DEBUG
, INFO
, WARN
, ERROR
, ASSERT
或SUPPRESS
。 有关这方面的更多信息,请参阅isLoggable()
方法的文档。
您可以使用setprop
命令临时设置属性。 例如:
C:\android>adb shell setprop log.tag.MyAppTag WARN C:\android>adb shell getprop log.tag.MyAppTag WARN
或者,您可以在文件“/data/local.prop”中指定它们,如下所示:
log.tag.MyAppTag=WARN
Android的更高版本似乎要求只读/data/local.prop 。 该文件在启动时读取,因此您需要在更新之后重新启动。 如果/data/local.prop
是世界可写的,它可能会被忽略。
最后,您可以使用System.setProperty()
方法以编程方式设置它们。
最简单的方法可能是在部署之前通过ProGuard运行编译的JAR,configuration如下:
-assumenosideeffects class android.util.Log { public static int v(...); }
除了所有其他ProGuard优化之外,这将会直接从字节码中删除任何详细的日志语句。
我采取了一个简单的路线 – 创build一个包装类,也使用可变参数列表。
public class Log{ public static int LEVEL = android.util.Log.WARN; static public void d(String tag, String msgFormat, Object...args) { if (LEVEL<=android.util.Log.DEBUG) { android.util.Log.d(tag, String.format(msgFormat, args)); } } static public void d(String tag, Throwable t, String msgFormat, Object...args) { if (LEVEL<=android.util.Log.DEBUG) { android.util.Log.d(tag, String.format(msgFormat, args), t); } } //...other level logging functions snipped
更好的方法是使用SLF4J API +它的一些实现。
对于Android应用程序,您可以使用以下内容:
- Android Logger是轻量级但易于configuration的SLF4J实现(<50 Kb)。
- LOGBack是最强大和最优化的实现,但其大小约为1Mb。
- 任何其他由你的口味:slf4j-android,slf4android。
你应该使用
if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "my log message"); }
用proguard去除日志logging(参见@Christopher的回答)非常简单而且快速,但是如果文件中有任何debugging日志logging,则会导致生成的堆栈跟踪不匹配。
相反,这是一种在开发和生产中使用不同日志logging级别的技术,假定proguard仅用于生产。 它通过查看proguard是否重命名给定的类名来识别生产(在本例中,我使用“com.foo.Bar” – 您将用一个完全合格的类名replace它,您知道它将被proguard重命名)。
这种技术使用公用日志logging。
private void initLogging() { Level level = Level.WARNING; try { // in production, the shrinker/obfuscator proguard will change the // name of this class (and many others) so in development, this // class WILL exist as named, and we will have debug level Class.forName("com.foo.Bar"); level = Level.FINE; } catch (Throwable t) { // no problem, we are in production mode } Handler[] handlers = Logger.getLogger("").getHandlers(); for (Handler handler : handlers) { Log.d("log init", "handler: " + handler.getClass().getName()); handler.setLevel(level); } }
Log4j或slf4j也可以和logcat一起用作Android中的日志框架。 请参阅android中的项目android-logging-log4j或log4j支持
标准的Android Log类有一个小小的替代品 – https://github.com/zserge/log
基本上你所要做的就是把导入android.util.Log
到trikita.log.Log
。 然后在你的Application.onCreate()
或者一些静态的initalizer中检查BuilConfig.DEBUG
或其他标志,并使用Log.level(Log.D)
或Log.level(Log.E)
来改变最小的日志级别。 您可以使用Log.useLog(false)
来Log.useLog(false)
日志logging。
可能是你可以看到这个日志扩展类: https : //github.com/dbauduin/Android-Tools/tree/master/logs 。
它使您能够对日志进行良好的控制。 例如,你可以禁用所有的日志或只是某些包或类的日志。
此外,它增加了一些有用的function(例如,你不必为每个日志传递标签)。
我创build了一个Utility / Wrapper来解决这个问题+logging周围的其他常见问题。
具有以下function的debugging实用程序:
- 由LogMode提供的Log类提供的常用function。
- 方法出入日志:可以通过交换机closures
- select性debugging:debugging特定的类。
- 方法执行时间测量:度量单个方法的执行时间,以及花费在一个类的所有方法上的集体时间。
如何使用?
- 将课程包含在您的项目中。
- 像使用android.util.Log方法一样使用它,以开始。
- 通过在应用程序中的方法的开始和结束处调用entry_log() – exit_log()方法来使用Entry-Exit日志function。
我试图让文档自我满足。
欢迎提出改进此function的build议。
免费使用/分享。
从GitHub下载。
这是一个更复杂的解决scheme。 您将获得完整的堆栈跟踪,只有在需要时才会调用toString()方法(性能)。 在生产模式下,属性BuildConfig.DEBUG将为false,因此所有跟踪和debugging日志都将被删除。 热点编译器有机会删除调用,因为最终的静态属性。
import java.io.ByteArrayOutputStream; import java.io.PrintStream; import android.util.Log; public class Logger { public enum Level { error, warn, info, debug, trace } private static final String DEFAULT_TAG = "Project"; private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info; private static boolean isEnabled(Level l) { return CURRENT_LEVEL.compareTo(l) >= 0; } static { Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name()); } private String classname = DEFAULT_TAG; public void setClassName(Class<?> c) { classname = c.getSimpleName(); } public String getClassname() { return classname; } public boolean isError() { return isEnabled(Level.error); } public boolean isWarn() { return isEnabled(Level.warn); } public boolean isInfo() { return isEnabled(Level.info); } public boolean isDebug() { return isEnabled(Level.debug); } public boolean isTrace() { return isEnabled(Level.trace); } public void error(Object... args) { if (isError()) Log.e(buildTag(), build(args)); } public void warn(Object... args) { if (isWarn()) Log.w(buildTag(), build(args)); } public void info(Object... args) { if (isInfo()) Log.i(buildTag(), build(args)); } public void debug(Object... args) { if (isDebug()) Log.d(buildTag(), build(args)); } public void trace(Object... args) { if (isTrace()) Log.v(buildTag(), build(args)); } public void error(String msg, Throwable t) { if (isError()) error(buildTag(), msg, stackToString(t)); } public void warn(String msg, Throwable t) { if (isWarn()) warn(buildTag(), msg, stackToString(t)); } public void info(String msg, Throwable t) { if (isInfo()) info(buildTag(), msg, stackToString(t)); } public void debug(String msg, Throwable t) { if (isDebug()) debug(buildTag(), msg, stackToString(t)); } public void trace(String msg, Throwable t) { if (isTrace()) trace(buildTag(), msg, stackToString(t)); } private String buildTag() { String tag ; if (BuildConfig.DEBUG) { StringBuilder b = new StringBuilder(20); b.append(getClassname()); StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4]; if (stackEntry != null) { b.append('.'); b.append(stackEntry.getMethodName()); b.append(':'); b.append(stackEntry.getLineNumber()); } tag = b.toString(); } else { tag = DEFAULT_TAG; } } private String build(Object... args) { if (args == null) { return "null"; } else { StringBuilder b = new StringBuilder(args.length * 10); for (Object arg : args) { if (arg == null) { b.append("null"); } else { b.append(arg); } } return b.toString(); } } private String stackToString(Throwable t) { ByteArrayOutputStream baos = new ByteArrayOutputStream(500); baos.toString(); t.printStackTrace(new PrintStream(baos)); return baos.toString(); } }
像这样使用:
Loggor log = new Logger(); Map foo = ... List bar = ... log.error("Foo:", foo, "bar:", bar); // bad example (avoid something like this) // log.error("Foo:" + " foo.toString() + "bar:" + bar);
在一个非常简单的日志logging场景中,在开发过程中,为了进行debugging,只需要尝试写入控制台,在生成构build之前进行search和replace可能是最简单的,并将所有对Log或System的调用注释掉。通过out.println。
例如,假设你没有使用“日志”。 在Log.d或Log.e等调用之外的任何地方,您都可以简单地在整个解决scheme中进行查找和replace,以取代“Log”。 与“/ /日志”。 注释掉所有的日志logging调用,或者在我的情况下,我只是使用System.out.println无处不在,所以在进行生产之前,我只是做一个完整的search并replace“System.out.println”,并用“//System.out.println”。
我知道这并不理想,如果能够查找和注释Log和System.out.println调用的能力已经内置到Eclipse中,那将是非常好的,但是直到发生这种情况时,最简单,最快和最好的方法是通过search和replace评论。 如果你这样做,你不必担心堆栈跟踪行号不匹配,因为你正在编辑你的源代码,而且你没有通过检查一些日志级别的configuration等来增加开销。
在我的应用程序中,我有一个包装Log类的类,它有一个名为“state”的静态布尔variables。 在我的代码中,在实际写入日志之前,我使用静态方法检查“状态”variables的值。 然后我有一个静态方法来设置“状态”variables,确保该值在应用程序创build的所有实例中是公共的。 这意味着我可以在一次调用中启用或禁用应用程序的所有日志logging – 即使应用程序正在运行。 用于支持电话…这意味着你必须坚持你的枪debugging时,而不是退步使用标准的日志类,但…
如果Java尚未分配值,那么Java解释布尔variables为false也是有用的(方便的),这意味着它可以保留为false,直到您需要打开日志:-)
我们可以在本地组件中使用类Log
,并将方法定义为v / i / e / d。 根据需要我们可以进一步打电话。
示例如下所示。
public class Log{ private static boolean TAG = false; public static void d(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.d(enable_tag, message+args); } public static void e(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.e(enable_tag, message+args); } public static void v(String enable_tag, String message,Object...args){ if(TAG) android.util.Log.v(enable_tag, message+args); } } if we do not need any print(s), at-all make TAG as false for all else remove the check for type of Log (say Log.d). as public static void i(String enable_tag, String message,Object...args){ // if(TAG) android.util.Log.i(enable_tag, message+args); }
这里的消息是为string
和和args
是你想要打印的值。
对于我来说,能够为每个TAG设置不同的日志级别通常很有用。
我正在使用这个非常简单的包装类:
public class Log2 { public enum LogLevels { VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN( android.util.Log.WARN), ERROR(android.util.Log.ERROR); int level; private LogLevels(int logLevel) { level = logLevel; } public int getLevel() { return level; } }; static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>(); public static void setLogLevel(String tag, LogLevels level) { logLevels.put(tag, level.getLevel()); } public static int v(String tag, String msg) { return Log2.v(tag, msg, null); } public static int v(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.VERBOSE) { return -1; } } return Log.v(tag, msg, tr); } public static int d(String tag, String msg) { return Log2.d(tag, msg, null); } public static int d(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.DEBUG) { return -1; } } return Log.d(tag, msg); } public static int i(String tag, String msg) { return Log2.i(tag, msg, null); } public static int i(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.INFO) { return -1; } } return Log.i(tag, msg); } public static int w(String tag, String msg) { return Log2.w(tag, msg, null); } public static int w(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.WARN) { return -1; } } return Log.w(tag, msg, tr); } public static int e(String tag, String msg) { return Log2.e(tag, msg, null); } public static int e(String tag, String msg, Throwable tr) { if (logLevels.containsKey(tag)) { if (logLevels.get(tag) > android.util.Log.ERROR) { return -1; } } return Log.e(tag, msg, tr); } }
现在只需在每个课程开始时设置每个TAG的日志级别:
Log2.setLogLevel(TAG, LogLevels.INFO);