我有很多日志语句要调试,例如。
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
在设备手机上部署此应用程序时,我想关闭可以启用/禁用日志记录的详细日志记录。
答案 0 :(得分:191)
Android Documentation says the following about Log Levels:
除了在开发期间,不应将详细编译到应用程序中。调试日志在运行时编译但被剥离。始终保留错误,警告和信息日志。
因此,您可能需要考虑删除日志详细日志记录语句possibly using ProGuard as suggested in another answer。
根据文档,您可以使用“系统属性”配置开发设备上的日志记录。要设置的属性为log.tag.<YourTag>
,应将其设置为以下值之一:VERBOSE
,DEBUG
,INFO
,WARN
,{{1} },ERROR
或ASSERT
。 More information on this is available in the documentation for the isLoggable()
method.
您可以使用SUPPRESS
命令临时设置属性。例如:
setprop
或者,您可以在文件'/data/local.prop'中指定它们,如下所示:
C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN
Android appear to require that /data/local.prop be read only的更高版本。此文件在引导时读取,因此您需要在更新后重新启动。如果log.tag.MyAppTag=WARN
是世界可写的,则可能会被忽略。
最后,您可以使用System.setProperty()
method以编程方式设置它们。
答案 1 :(得分:88)
最简单的方法可能是在部署之前通过ProGuard运行已编译的JAR,其配置如下:
-assumenosideeffects class android.util.Log {
public static int v(...);
}
除了所有其他ProGuard优化之外,它将直接从字节码中删除任何详细的日志语句。
答案 2 :(得分:78)
一种常见的方法是创建一个名为loglevel的int,并根据loglevel定义其调试级别。
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
稍后,您只需更改所有调试输出级别的LOGLEVEL即可。
答案 3 :(得分:18)
我采用了一个简单的路径 - 创建一个包含可变参数列表的包装类。
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
答案 4 :(得分:10)
更好的方法是使用SLF4J API +的一些实现。
对于Android应用程序,您可以使用以下内容:
答案 5 :(得分:8)
你应该使用
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
答案 6 :(得分:4)
使用proguard删除日志记录(请参阅@Christopher的回答)既简单又快速,但如果文件中有任何调试日志记录,则会导致生产中的堆栈跟踪与源不匹配。
相反,这是一种在开发与生产中使用不同日志记录级别的技术,假设proguard仅用于生产。它通过查看proguard是否已重命名给定的类名来识别生产(在示例中,我使用“com.foo.Bar” - 您将使用您知道将由proguard重命名的完全限定的类名替换它)。
这种技术利用了公共记录。
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);
}
}
答案 7 :(得分:3)
Log4j或slf4j也可以与logcat一起用作Android中的日志框架。查看项目android-logging-log4j或log4j support in android
答案 8 :(得分:3)
标准的android Log类有一个小小的替代品 - https://github.com/zserge/log
基本上,您所要做的就是将导入从android.util.Log
替换为trikita.log.Log
。然后在Application.onCreate()
或静态初始化器中检查BuilConfig.DEBUG
或任何其他标志,并使用Log.level(Log.D)
或Log.level(Log.E)
更改最小日志级别。您可以使用Log.useLog(false)
来禁用日志记录。
答案 9 :(得分:2)
您可以看到此Log扩展类:https://github.com/dbauduin/Android-Tools/tree/master/logs。
它使您可以对日志进行精细控制。 例如,您可以禁用所有日志或仅禁用某些包或类的日志。
此外,它添加了一些有用的功能(例如,您不必为每个日志传递标记)。
答案 10 :(得分:2)
我创建了一个Utility / Wrapper来解决这个问题+ Logging周围的其他常见问题。
具有以下功能的调试实用程序:
如何使用?
我试图让文档自给自足。
欢迎提出改进此实用程序的建议。
免费使用/分享。
从GitHub下载。
答案 11 :(得分:2)
这是一个更复杂的解决方案。您将获得完整的堆栈跟踪,并且仅在需要时才会调用toString()方法(性能)。生产模式中的属性BuildConfig.DEBUG将为false,因此将删除所有跟踪和调试日志。热点编译器有机会删除调用,因为关闭了最终的静态属性。
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);
答案 12 :(得分:1)
在我的应用程序中,我有一个包装Log类的类,它有一个名为“state”的静态布尔变量。在我的代码中,我在实际写入Log之前使用静态方法检查“state”变量的值。然后我有一个静态方法来设置“状态”变量,该变量确保该值在应用程序创建的所有实例中都是通用的。这意味着我可以在一次调用中启用或禁用应用程序的所有日志记录 - 即使应用程序正在运行。对支持调用很有用......它确实意味着你必须在调试时坚持你的枪,而不是回归到使用标准的Log类,但是......
如果没有为一个值分配值,那么Java将boolean var解释为false也很有用(方便),这意味着它可以保留为false,直到您需要打开日志记录: - )
答案 13 :(得分:1)
在一个非常简单的日志记录场景中,您实际上只是在开发期间尝试写入控制台以进行调试,最简单的方法是在生成构建之前进行搜索和替换,并注释掉所有对Log的调用。或System.out.println。
例如,假设您没有使用“日志”。在调用Log.d或Log.e等之外的任何地方,您只需在整个解决方案中进行查找和替换即可替换“Log”。用“// Log。”注释掉你所有的日志记录调用,或者在我的情况下,我只是在任何地方使用System.out.println,所以在开始生产之前我只需要进行全面搜索并替换“System.out.println”并替换为“//System.out.println”。
我知道这不太理想,如果找到并注释掉对Log和System.out.println的调用的能力是内置到Eclipse中的话会很好,但在此之前,最简单,最快速,最好的方法是这是通过搜索和替换注释掉。如果这样做,您不必担心堆栈跟踪行号不匹配,因为您正在编辑源代码,并且您不会通过检查某些日志级别配置等来添加任何开销。
答案 14 :(得分:1)
我们可以在本地组件中使用类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
是您要打印的值。
答案 15 :(得分:0)
对我来说,能够为每个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);
答案 16 :(得分:0)
另一种方法是使用具有打开和关闭日志功能的日志记录平台。有时甚至在生产应用程序上也可以提供很大的灵活性,该应用程序应打开和关闭日志,具体取决于您遇到的问题 例如:
答案 17 :(得分:0)
但是,System.setProperty()
存储的属性集与可以在ADB Shell上发出的getprop
命令访问的属性相比,完全不同。因此,即使您尝试使用System.setProperty()
存储标签的日志级别,它也将不起作用。
如前所述,您可以使用adb shell setprop
adb shell setprop log.tag.<YOUR_LOG_TAG> <LEVEL>
主日志级别
日志级别值注释
ERROR 6 Errors<br/>
WARN 5 Warnings
INFO 4 Information messages <-Default level of any tag (any level above and including INFO)
DEBUG 3 Debug output
VERBOSE 2 For development only
了解更多here
答案 18 :(得分:0)
https://limxtop.blogspot.com/2019/05/app-log.html
请阅读本文,其中提供完整的工具:
setprop log.tag.<YOUR_LOG_TAG> <LEVEL>
启用DEBUG和VERBOSE日志。