Как включить/отключить уровни журналов в Android?

У меня есть множество операторов регистрации для отладки, например.

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

при развертывании этого приложения на телефоне устройства я хочу отключить подробный журнал, откуда я могу включить/отключить протоколирование.

Ответ 1

Общим способом является создание имени именованного логического уровня и определение его уровня отладки на основе 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 для всех уровней вывода отладки.

Ответ 2

Документация Android содержит следующие сведения о уровнях ведения журнала:

Verbose никогда не следует компилировать в приложение, кроме как во время разработки. Журналы отладки компилируются, но удаляются во время выполнения. Журналы ошибок, предупреждений и информации всегда сохраняются.

Таким образом, вы можете захотеть удалить дескрипторы журнала Verbose, возможно, используя ProGuard, как это предлагается в другом ответе.

В соответствии с документацией вы можете настроить ведение журнала на устройстве разработки с помощью System Properties. Свойство для установки равно 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().

Ответ 3

Самый простой способ - запустить ваш скомпилированный JAR через ProGuard перед развертыванием с конфигурацией вроде:

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

Это будет — кроме всех других оптимизаций ProGuard — удалите любые подробные записи журнала непосредственно из байт-кода.

Ответ 4

Я сделал простой маршрут - создав класс-оболочку, который также использует списки переменных параметров.

 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

Лучше всего использовать API SLF4J + часть его реализации.

Для приложений Android вы можете использовать следующее:

  • Android Logger - это легкая, но простая в использовании реализация SLF4J (< 50 Kb).
  • LOGBack - самая мощная и оптимизированная реализация, но ее размер составляет около 1 МБ.
  • Любой другой по вашему вкусу: slf4j-android, slf4android.

Ответ 6

Вы должны использовать

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "my log message");
    }

Ответ 7

Удаление журнала с помощью 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);
    }
}

Ответ 9

Существует небольшая замена для стандартного класса Android 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

Возможно, вы можете увидеть этот класс расширения журнала: https://github.com/dbauduin/Android-Tools/tree/master/logs.

Это позволяет вам иметь прекрасный контроль над журналами. Вы можете, например, отключить все журналы или только журналы некоторых пакетов или классов.

Кроме того, он добавляет некоторые полезные функции (например, вам не нужно передавать тег для каждого журнала).

Ответ 11

Вот более сложное решение. Вы получите полную трассировку стека, и метод toString() будет вызываться только при необходимости (Performance). Атрибут BuildConfig.DEBUG будет ложным в рабочем режиме, поэтому все журналы трассировки и отладки будут удалены. Компилятор горячей точки имеет возможность удалить вызовы из-за окончательных статических свойств.

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

Я создал утилиту /Wrapper, которая решает эту проблему + другие общие проблемы, связанные с журналом.

Утилита отладки со следующими функциями:

  • Обычные функции, предоставляемые классом Log, обернутые LogMode s.
  • Журнал ввода-выхода метода: может быть отключен коммутатором
  • Селективная отладка: отладка определенных классов.
  • Измерение времени выполнения метода: измерение времени выполнения для отдельных методов, а также коллективное время, потраченное на все методы класса.

Как использовать?

  • Включите класс в свой проект.
  • Используйте его, как вы используете методы android.util.Log, для начала.
  • Используйте функцию журналов Entry-Exit, разместив вызовы методов entry_log() - exit_log() в начале и конце методов в вашем приложении.

Я попытался сделать документацию самодостаточной.

Предложения по улучшению этой утилиты приветствуются.

Свободно использовать/делиться.

Загрузите его из GitHub.

Ответ 13

В очень простом сценарии ведения журнала, где вы буквально просто пытаетесь писать на консоль во время разработки для целей отладки, проще всего просто выполнить поиск и заменить перед вашей производственной сборкой и прокомментировать все вызовы в журнал или System.out.println.

Например, если вы не используете "Журнал". где-либо вне вызова Log.d или Log.e и т.д., вы можете просто найти и заменить все решение для замены "Log". с "//Log". прокомментировать все ваши вызовы регистрации, или, в моем случае, я просто использую System.out.println всюду, поэтому, прежде чем приступать к производству, я просто сделаю полный поиск и замените для "System.out.println" и заменим на "//System.out.println".

Я знаю, что это не идеально, и было бы неплохо, если бы способность находить и прокомментировать вызовы Log и System.out.println были встроены в Eclipse, но до тех пор, пока это не будет самым простым и быстрым и лучшим способом сделайте это, чтобы прокомментировать поиск и заменить. Если вы это сделаете, вам не нужно беспокоиться о несоответствии номеров строк трассировки стека, потому что вы редактируете исходный код, и вы не добавляете никаких накладных расходов, проверяя конфигурацию уровня журнала и т.д.

Ответ 14

В моих приложениях у меня есть класс, который обертывает класс журнала, который имеет статический логический var, называемый "state". Во всем моем коде я проверяю значение переменной "состояние", используя статический метод, прежде чем писать в журнал. Затем у меня есть статический метод для установки переменной состояния, которая гарантирует, что значение является общим для всех экземпляров, созданных приложением. Это означает, что я могу включить или отключить все протоколирование для приложения одним звонком - даже когда приложение запущено. Полезно для вызовов поддержки... Это означает, что вы должны придерживаться своего оружия при отладке, а не регрессировать, чтобы использовать стандартный класс журнала, хотя...

Также полезно (удобно), что Java интерпретирует логический var как false, если ему не присвоено значение, что означает, что он может быть оставлен как false до тех пор, пока вам не понадобится включить ведение журнала: -)

Ответ 15

Мы можем использовать класс 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

Для меня часто бывает полезно установить разные уровни журналов для каждой 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);

Ответ 17

Другой способ - использовать платформу регистрации, которая имеет возможность открывать и закрывать журналы. Это может дать большую гибкость иногда даже для производственного приложения, журналы которого должны быть открыты, а какие - закрыты, в зависимости от того, какие у вас проблемы, например:

Ответ 18

Однако System.setProperty() хранит совершенно другой набор свойств по сравнению со свойствами, доступ к которым может получить команда getprop которая может быть выдана в оболочке ADB. Следовательно, даже если вы попытаетесь использовать 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 

Узнайте больше здесь