Как включить / отключить уровни журнала в Android?
У меня есть много операторов ведения журнала для отладки, например.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
при развертывании этого приложения на телефоне устройства я хочу отключить подробное ведение журнала, где я могу включить / отключить ведение журнала.
16 ответов:
распространенным способом является создание int с именем loglevel и определение его уровня отладки на основе 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позже вы можете просто изменить уровень журнала для всех выходных данных отладки.
The документация Android говорит следующее об уровнях журнала:
подробный никогда не должен быть скомпилирован в приложение, кроме как во время разработки. Журналы отладки компилируются, но удаляются во время выполнения. Журналы ошибок, предупреждений и информации всегда хранятся.
таким образом, вы можете рассмотреть вопрос о снятии подробных операторов регистрации журнала,возможно использование ProGuard как предложено в другом ответ.
согласно документации, вы можете настроить ведение журнала на устройстве разработки с помощью системных свойств. Свойство для установки является
log.tag.<YourTag>и он должен быть установлен в одно из следующих значений:VERBOSE,DEBUG,INFO,WARN,ERROR,ASSERTилиSUPPRESS. более подробная информация об этом доступна в документации дляisLoggable()метод.вы можете установить свойства временно с помощью . Для пример:
C:\android>adb shell setprop log.tag.MyAppTag WARN C:\android>adb shell getprop log.tag.MyAppTag WARNкроме того, вы можете указать их в файле '/data/local.опора ' следующим образом:
log.tag.MyAppTag=WARNболее поздние версии Android кажется, требуется, чтобы /data / local.опора только для чтения. Этот файл читается во время загрузки, поэтому вам нужно будет перезапустить его после обновления. Если
/data/local.propявляется ли мир доступным для записи, он, скорее всего, будет проигнорирован.наконец, вы можете установить их программно с помощью
System.setProperty()метод.
самый простой способ, вероятно, запустить скомпилированную банку через должны быть перед развертыванием, с конфигурацией, как:
-assumenosideeffects class android.util.Log { public static int v(...); }Это будет-помимо всех других оптимизаций ProGuard-удалять любые подробные инструкции журнала непосредственно из байт-кода.
Я взял простой маршрут-создание класса-оболочки, который также использует списки переменных параметров.
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 - это легкая, но простая в настройке реализация SLF4J (
- LOGBack-самая мощная и оптимизированная реализация, но ее размер составляет около 1 Мб.
- любой другой на ваш вкус: slf4j-android, slf4android.
удаление журнала с помощью proguard (см. ответ от @Christopher ) было простым и быстрым, но это привело к тому, что трассировки стека из производства не соответствовали источнику, если в файле было какое-либо ведение журнала отладки.
вместо этого, вот метод, который использует различные уровни ведения журнала в разработке против производства, предполагая, что proguard используется только в производстве. Он распознает производство, видя, переименовал ли proguard имя данного класса (в примере я использую "com.foo.Бар"--вы бы замените это полным именем класса, которое, как вы знаете, будет переименовано proguard).
этот метод использует ведение журнала commons.
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 также могут использоваться в качестве фреймворков регистрации в Android вместе с logcat. Смотрите проект android-logging-log4j или поддержка log4j в android
есть крошечная замена для стандартного класса журнала android -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)чтобы вообще отключить ведение журнала.
может быть, вы можете увидеть этот класс расширения журнала:https://github.com/dbauduin/Android-Tools/tree/master/logs.
Это позволяет иметь точный контроль над журналами. Например, можно отключить все журналы или только журналы некоторых пакетов или классов.
кроме того, он добавляет некоторые полезные функции (например, вам не нужно передавать тег для каждого журнала).
Я создал утилиту / обертку, которая решает эту проблему + другие распространенные проблемы вокруг ведения журнала.
утилита отладки со следующими функциями:
- обычные функции, предоставляемые классом журнала, обернутым вокруг LogMode s.
- вход метода-журналы выхода: смогите быть повернуто переключателем
- выборочная отладка: отладка определенных классов.
- измерение времени выполнения метода: измерьте время выполнения для индивидуальные методы, а также коллективное время, затраченное на все методы класса.
Как Использовать?
- добавить класс в проект.
- используйте его, как вы используете android.утиль.Методы журнала, для начала.
- используйте функцию вход-выход журналов, размещая вызовы методов entry_log () - exit_log () в начале и конце методов в вашем приложении.
Я попытался сделать документацию самостоятельно достаточно.
предложения по улучшению программы приветствуются.
бесплатно использовать / поделиться.
скачать с GitHub.
вот более сложное решение. Вы получите полную трассировку стека и метод 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);
в очень простом сценарии ведения журнала, где вы буквально просто пытаетесь писать в консоль во время разработки для целей отладки, может быть проще всего просто выполнить поиск и замену перед вашей производственной сборкой и закомментировать все вызовы журнала или системы.из.println.
например, предполагая, что вы не использовали "журнал.- куда угодно, только не в Журнал вызовов.d или журнал.e и т. д. Вы можете просто найти и заменить все решение, чтобы заменить " журнал." с бревнами." чтобы прокомментировать все ваши звонки в журнал, или в моем случае я просто использую систему.из.println везде, поэтому перед выходом на производство я просто сделаю полный поиск и заменю "систему.из.println "и заменить на" / / Система.из.println".
Я знаю, что это не идеально, и было бы неплохо, если бы возможность находить и комментировать вызовы в журнал и систему.из.println были встроены в Eclipse, но до тех пор, пока это не произойдет, самый простой и быстрый и лучший способ сделать это-прокомментировать поиск и заменить. Если вы это сделаете, вам не нужно беспокоиться о несоответствии номеров строк трассировки стека, потому что вы редактируете свой исходный код, и вы не добавляете никаких накладных расходов, проверяя некоторую конфигурацию уровня журнала и т. д.
в моих приложениях у меня есть класс, который обертывает класс журнала, который имеет статический логический var под названием "Состояние". На протяжении всего моего кода я проверяю значение переменной "state" с помощью статического метода перед фактической записью в журнал. Затем у меня есть статический метод для установки переменной "состояние", которая гарантирует, что значение является общим для всех экземпляров, созданных приложением. Это означает, что я могу включить или отключить все ведение журнала для приложения в одном вызове - даже когда приложение работает. Полезно для звонков в службу поддержки... Оно означает ли это, что вы должны придерживаться своего оружия при отладке и не регрессировать к использованию стандартного класса журнала...
также полезно (удобно), что Java интерпретирует логический var как 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- это значение, которое вы хотите напечатать.
для меня часто полезно иметь возможность устанавливать разные уровни журнала для каждого тега.
Я использую этот очень простой класс-оболочку:
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);
Comments