Как включить / отключить уровни журнала в Android?
Я имею много записей журнала для отладки, например.
Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");
при развертывании этого приложения на телефоне устройства я хочу отключить подробное ведение журнала, откуда я могу включить / отключить ведение журнала.
17 ответов
Распространенным способом является создание 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
Позже вы можете просто изменить LOGLEVEL для всех уровней вывода отладки.
Документация Android говорит следующее об Уровнях Журнала:
Подробно никогда не следует компилировать в приложение, кроме как во время разработки. Журналы отладки компилируются, но удаляются во время выполнения. Журналы ошибок, предупреждений и информации всегда сохраняются.
Таким образом, вы можете захотеть удалить из журнала подробные записи журналирования, возможно, используя ProGuard, как предложено в другом ответе.
Согласно документации, вы можете настроить ведение журнала на устройстве разработки, используя Свойства системы. Свойство для установки 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()
метод.
Самый простой способ - это, вероятно, запустить ваш скомпилированный JAR через ProGuard перед развертыванием с такой конфигурацией, как:
-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 (< 50 Кб).
- LOGBack - самая мощная и оптимизированная реализация, но ее размер составляет около 1 Мб.
- Любой другой на ваш вкус: slf4j-android, slf4android.
Вы должны использовать
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "my log message");
}
Удаление журналирования с помощью 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);
}
}
Существует небольшая замена стандартного класса журнала 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)
отключить ведение журнала вообще.
Log4j или slf4j также могут быть использованы в качестве каркасов ведения журналов в Android вместе с logcat. Смотрите поддержку проекта http://code.google.com/p/android-logging-log4j/ или log4j в android
Вот более сложное решение. Вы получите полную трассировку стека, и метод toString() будет вызываться только при необходимости (Performance). Атрибут 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);
Я создал Utility/Wrapper, который решает эту проблему + другие распространенные проблемы, связанные с ведением журнала.
Утилита отладки со следующими функциями:
- Обычные функции, предоставляемые классом Log, обернуты в LogMode s.
- Журналы метода входа-выхода: могут быть отключены с помощью переключателя
- Выборочная отладка: отладка определенных классов.
- Измерение времени выполнения метода: мера Время выполнения для отдельных методов, а также общее время, потраченное на все методы класса.
Как пользоваться?
- Включите класс в свой проект.
- Используйте его так же, как вы используете методы android.util.Log, чтобы начать с.
- Используйте функцию журналов Entry-Exit, вызывая методы entry_log()-exit_log() в начале и в конце методов вашего приложения.
Я попытался сделать документацию самодостаточной.
Предложения по улучшению этой утилиты приветствуются.
Бесплатно использовать / поделиться.
Загрузите его с GitHub.
Может быть, вы можете увидеть этот класс расширения журнала: https://github.com/dbauduin/Android-Tools/tree/master/logs.
Это позволяет вам точно контролировать журналы. Например, вы можете отключить все журналы или только журналы некоторых пакетов или классов.
Более того, он добавляет некоторые полезные функции (например, вам не нужно передавать тег для каждого журнала).
Мы можем использовать класс 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
это значение, которое вы хотите напечатать.
В очень простом сценарии ведения журнала, когда вы буквально просто пытаетесь записать данные на консоль во время разработки для целей отладки, может быть проще всего выполнить поиск и замену перед сборкой и закомментировать все вызовы Log или System.out.println.
Например, если вы не используете "Журнал". В любом месте, кроме вызова Log.d или Log.e и т. д., вы можете просто найти и заменить все решение, чтобы заменить "Журнал". с "//Log." чтобы закомментировать все ваши звонки в журнале, или в моем случае я просто использую System.out.println везде, поэтому перед тем, как перейти к производству, я просто сделаю полный поиск и заменит "System.out.println" и заменит на "//System.out.println".
Я знаю, что это не идеально, и было бы неплохо, если бы в Eclipse была встроена возможность находить и комментировать вызовы Log и System.out.println, но пока это не произойдет, самый простой и быстрый и лучший способ сделать это закомментировать путем поиска и замены. Если вы сделаете это, вам не придется беспокоиться о несоответствии номеров строк трассировки стека, потому что вы редактируете свой исходный код, и вы не добавляете никаких накладных расходов, проверяя некоторую конфигурацию уровня журнала и т. Д.
В моих приложениях у меня есть класс, который оборачивает класс Log, который имеет статическую логическую переменную, называемую "state". Во всем моем коде я проверяю значение переменной "state" статическим методом, прежде чем записывать его в журнал. Затем у меня есть статический метод для установки переменной "состояние", которая гарантирует, что значение является общим для всех экземпляров, созданных приложением. Это означает, что я могу включить или отключить все журналы для приложения в один вызов - даже когда приложение работает. Полезно для звонков в службу поддержки... Это означает, что вы должны придерживаться своего оружия при отладке, а не возвращаться к использованию стандартного класса Log, хотя...
Также полезно (удобно), что Java интерпретирует логическое значение var как false, если ему не было присвоено значение, что означает, что его можно оставить как false до тех пор, пока вам не потребуется включить ведение журнала:-)
Для меня часто полезно иметь возможность устанавливать разные уровни журнала для каждого тега.
Я использую этот очень простой класс оболочки:
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);
https://limxtop.blogspot.com/2019/05/app-log.html
Прочтите эту статью, где дается полный комплект:
- Для отладочной версии будут выведены все логи;
- Для релизной версии по умолчанию будут выводиться только те журналы, уровень которых выше DEBUG (exclude). Между тем, журналы DEBUG и VERBOSE можно включить через
setprop log.tag.<YOUR_LOG_TAG> <LEVEL>
во время работы.
Другой способ - использовать платформу регистрации, которая имеет возможность открывать и закрывать журналы. Это может дать большую гибкость иногда даже для производственного приложения, журналы которого должны быть открыты, а какие - закрыты, в зависимости от того, какие у вас проблемы, например:
- лесоруб
- Судовая книга (отказ от ответственности: я автор этого пакета)