代码之家  ›  专栏  ›  技术社区  ›  d-man

如何在Android中启用/禁用日志级别?

  •  146
  • d-man  · 技术社区  · 15 年前

    例如,我有很多要调试的日志语句。

    Log.v(TAG, "Message here");
    Log.w(TAG, " WARNING HERE");
    

    在设备电话上部署此应用程序时,我希望关闭详细日志记录,从中可以启用/禁用日志记录。

    17 回复  |  直到 6 年前
        1
  •  78
  •   matt5784    10 年前

    一种常见的方法是生成名为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
    

    稍后,您可以更改所有调试输出级别的日志级别。

        2
  •  190
  •   Community paulsm4    7 年前

    这个 Android Documentation says the following about Log Levels :

    除非在开发期间,否则不应将verbose编译到应用程序中。调试日志在中编译,但在运行时被剥离。始终保留错误、警告和信息日志。

    因此,您可能需要考虑将日志详细日志语句剥离出来, possibly using ProGuard as suggested in another answer .

    根据文档,您可以使用系统属性在开发设备上配置日志记录。要设置的属性是 log.tag.<YourTag> 应设置为以下值之一: VERBOSE , DEBUG , INFO , WARN , ERROR , ASSERT SUPPRESS . More information on this is available in the documentation for the isLoggable() method.

    可以使用 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的更新版本 appear to require that /data/local.prop be read only . 此文件在启动时读取,因此更新后需要重新启动。如果 /data/local.prop 是世界可写的,很可能会被忽略。

    最后,您可以使用 System.setProperty() method .

        3
  •  88
  •   Christopher Orr    15 年前

    最简单的方法可能是通过 ProGuard 在部署之前,使用如下配置:

    -assumenosideeffects class android.util.Log {
        public static int v(...);
    }
    

    除了所有其他的Proguard优化之外,这将直接从字节码中删除任何详细的日志语句。

        4
  •  18
  •   sschuberth    9 年前

    我采用了一种简单的方法——创建一个包装类,它还使用变量参数列表。

     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
    
        5
  •  10
  •   Fortess Nsk    11 年前

    更好的方法是使用slf4j api+它的一些实现。

    对于Android应用程序,您可以使用以下功能:

    1. Android Logger 是一种轻量但易于配置的SLF4J实现(50 KB)。
    2. logback是最强大和优化的实现,但其大小约为1 MB。
    3. 任何其他你喜欢的:slf4j安卓,slf4android。
        6
  •  8
  •   Diego Torres Milano    15 年前

    你应该使用

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "my log message");
        }
    
        7
  •  4
  •   Mr_and_Mrs_D    11 年前

    用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);
        }
    }
    
        8
  •  3
  •   Community paulsm4    7 年前

    log4j或slf4j也可以与logcat一起用作Android中的日志框架。看到这个项目 android-logging-log4j log4j support in android

        9
  •  3
  •   Eric Weyant    9 年前

    标准android日志类的替代品略有下降。- 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) 完全禁用日志记录。

        10
  •  2
  •   Donkey    11 年前

    可能您可以看到这个日志扩展类: https://github.com/dbauduin/Android-Tools/tree/master/logs .

    它使您能够对日志进行精细的控制。 例如,您可以禁用所有日志,或者仅禁用某些包或类的日志。

    此外,它还添加了一些有用的功能(例如,不必为每个日志传递标记)。

        11
  •  2
  •   Vinay W    11 年前

    我创建了一个实用程序/包装器,它解决了这个问题以及日志记录的其他常见问题。

    具有以下功能的调试实用程序:

    • 日志类提供的通常特性 日志模式 S.
    • 方法入口退出日志:可以通过开关关闭
    • 选择性调试:调试特定类。
    • 方法执行时间度量:度量单个方法的执行时间,以及在类的所有方法上花费的集体时间。

    如何使用?

    • 在项目中包含类。
    • 像使用android.util.log方法一样使用它。
    • 通过在应用程序中方法的开始和结束处调用entry_log()-exit_log()方法,可以使用entry exit logs功能。

    我试图使文件具有自足性。

    欢迎提出改进此实用程序的建议。

    免费使用/分享。

    从下载 GitHub .

        12
  •  2
  •   Andreas Mager    10 年前

    这里有一个更复杂的解决方案。您将获得完整的堆栈跟踪,并且仅在需要时调用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); 
    
        13
  •  1
  •   Jim    11 年前

    在一个非常简单的日志场景中,您实际上只是为了调试而在开发过程中尝试写入控制台,在生产构建之前进行搜索和替换,并注释掉所有对Log或System.out.println的调用可能是最简单的。

    例如,假设您没有在log d.d或log e.e等调用之外的任何地方使用“log.”,您只需在整个解决方案中执行查找和替换操作,将“log.”替换为“//log”。若要注释掉您的所有日志调用,或者在我的情况下,我只是在任何地方使用system.out.println,所以在开始生产之前,我只需执行完整的s搜索并替换为“system.out.println”,替换为“//system.out.println”。

    我知道这并不理想,如果能找到并评论对log和system.out.println的调用的能力是内置在Eclipse中的,那就太好了,但是在这之前,最简单、最快速和最好的方法是通过搜索和替换来评论。如果这样做,就不必担心堆栈跟踪行号不匹配,因为您正在编辑源代码,并且通过检查一些日志级配置等不会增加任何开销。

        14
  •  1
  •   Darnst    11 年前

    在我的应用程序中,我有一个类,它包含一个名为“state”的静态布尔变量的日志类。在整个代码中,在实际写入日志之前,我使用静态方法检查“state”变量的值。然后,我有一个静态方法来设置“state”变量,它确保该值在应用程序创建的所有实例中都是通用的。这意味着我可以在一次通话中启用或禁用应用程序的所有日志记录,即使应用程序正在运行。对支持呼叫有用…它确实意味着在调试时必须坚持使用枪,而不是回归到使用标准的日志类。

    它也很有用(方便),如果没有分配一个值,Java将布尔var解释为false,这意味着它可以被保留为假,直到需要打开日志记录:

        15
  •  1
  •   Irshad huMpty duMpty    9 年前

    我们可以上课 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 是要打印的值。

        16
  •  0
  •   Jack Miller    10 年前

    对于我来说,能够为每个标记设置不同的日志级别通常很有用。

    我正在使用这个非常简单的包装类:

    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);
        }
    
    }
    

    现在只需在每个类的开头设置每个标记的日志级别:

    Log2.setLogLevel(TAG, LogLevels.INFO);
    
        17
  •  0
  •   Elisha Sterngold    6 年前

    另一种方法是使用具有打开和关闭日志功能的日志平台。有时甚至在生产应用程序上,这可以提供很大的灵活性,哪些日志应该打开,哪些日志应该关闭,这取决于您有哪些问题。 例如: