2010-01-07 2 views
142

У меня есть много операторов регистрации для отладки, например.Как включить или отключить уровни журналов в Android?

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

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

ответ

79

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

+1

приятно, но как бы вы отключили DEBUG в своем примере, но все еще показывали предупреждения .... –

+1

Не были бы ли инструкции if в байт-коде .apk? Я думал, что мы хотели (как правило) отключить ведение журнала, когда приложение было развернуто, но оператор if не будет удален. – chessofnerd

+2

в вашем примере будут отображаться сообщения DEBUG, а WARNs - нет? разве вы обычно не хотели бы наоборот? – Sam

88

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

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

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

+0

. Он содержит файл log.property, где мы можем определить настройки. –

+1

Удаление строк с помощью proguard означает, что ваши стеки следов от производства могут не совпадать с вашим кодом. – larham1

+3

@ larham1: ProGuard действует на байт-код, поэтому я бы предположил, что удаление вызовов регистрации не повлияет на метаданные встроенных строк. –

183

Android Documentation says the following about Log Levels:

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

Поэтому, возможно, вы захотите рассмотреть вопрос об изъятии выписок из журнала 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.

+4

У меня был такой же опыт; в документах API довольно неясно, как именно это должно работать и даже, похоже, упоминается большинство констант 'android.util.Config', которые устарели. Жестко закодированные значения, указанные в документах API, бесполезны, поскольку они (предположительно) меняются по строкам. Следовательно, путь ProGuard казался лучшим решением для нас. –

+2

Удалось ли вам настраивать ведение журнала Android с помощью/data/local.prop, метод setprop или с помощью System.setProperty? У меня довольно много проблем с получением Log.isLoggable (TAG, VERBOSE), чтобы вернуть true для меня. – seanoshea

+2

Я получил отладку android отладки. Фокус в том, что когда вы вызываете что-то вроде Log.d («xyz»), сообщение записывается в logcat, даже если отладка отключена для регистратора. Это означает, что фильтрация обычно происходит после записи. Для фильтрации перед чем-то вроде Log.isLoggable (TAG, Log.VERBOSE)) {Log.v (TAG, «мое сообщение в журнале»); } необходим. Это, как правило, довольно утомительно. Я использую модифицированную версию slf4j-android, чтобы получить то, что я хочу. – phreed

8

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

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

Как настроить вывод isLoggable? Являются ли debug и verbose not loggable, когда isDebugable задано false в манифесте? – OneWorld

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 
+1

Как я уже говорил выше. Я использовал модифицированную версию slf4j-android для реализации этой техники. – phreed

+3

Существует большая проблема по этому поводу, см. Http://stackoverflow.com/questions/2446248/remove-all-debug-logging-calls-before-publishing-are-there-tools-to-do-this?lq= 1 # comment8849301_4592958 – OneWorld

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

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

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

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

На Android вам придется использовать ['logback-android'] (http://tony.github.com/logback-android) (так как' logback' собственно несовместим). 'logback-android-1.0.10-1.jar' составляет 429 Кбайт, что не так уж плохо, учитывая предоставленные функции, но большинство разработчиков будут использовать Proguard для оптимизации своего приложения. – user46874

+0

Это не избавляет вас от использования операторов if, чтобы проверить уровень журнала перед протоколированием. См. Http://stackoverflow.com/questions/4958860/should-i-comment-my-log-calls-when-creating-my-final-package/4959362?noredirect1_comment24952637_4959362 – OneWorld

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, но до тех пор, пока это не будет самым простым и быстрым и лучшим способом для этого нужно прокомментировать поиск и заменить. Если вы это сделаете, вам не нужно беспокоиться о несоответствии номеров строк трассировки стека, потому что вы редактируете исходный код, и вы не добавляете никаких накладных расходов, проверяя конфигурацию уровня журнала и т. Д.

2

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

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

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

2

Это более сложное решение. Вы получите полную трассировку стека, и метод 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); 
1

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

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

2

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

отладки утилита со следующими характеристиками:

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

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

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

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

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

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

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

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

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

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 значение, которое вы хотите напечатать.

Смежные вопросы