Как удалить все вызовы журнала отладки перед созданием релизной версии приложения для Android?
Согласно Google, я должен "деактивировать любые вызовы методов Log в исходном коде" перед публикацией моего приложения для Android в Google Play. Извлечение из раздела 3 контрольного списка публикации:
Убедитесь, что вы отключили ведение журнала и отключили параметр отладки, прежде чем создавать приложение для выпуска. Вы можете отключить ведение журнала, удалив вызовы методов журнала в исходных файлах.
Мой проект с открытым исходным кодом велик, и каждый раз, когда я выпускаю его, больно делать это вручную. Кроме того, удаление строки журнала потенциально сложно, например:
if(condition)
Log.d(LOG_TAG, "Something");
data.load();
data.show();
Если я прокомментирую строку журнала, то условие применяется к следующей строке, и вероятность того, что load() не вызывается. Достаточно ли редки такие ситуации, чтобы я мог решить, что их не должно быть?
Итак, есть ли лучший способ сделать это на уровне исходного кода? Или, может быть, какой-нибудь умный синтаксис ProGuard для эффективного, но безопасного удаления всех строк журнала?
Ответы
Ответ 1
Я нахожу гораздо более легкое решение - забыть все проверки if
по всему месту и просто использовать ProGuard, чтобы вычеркнуть любой метод Log.d()
или Log.v()
вызывается, когда мы вызываем наш Ant release
target.
Таким образом, у нас всегда есть информация об отладке, которая выводится для регулярных сборок и не требует каких-либо изменений кода для релизов. ProGuard также может выполнять несколько проходов по байт-коду для удаления других нежелательных операторов, пустых блоков и может автоматически включать короткие методы, если это необходимо.
Например, здесь очень простая конфигурация ProGuard для Android:
-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5
-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
Таким образом, вы сохраните это в файле, а затем вызовите ProGuard из Ant, передав ваш JAR-код и JAR-платформу Android, которую вы используете.
См. также примеры в руководстве ProGuard.
Обновление (4,5 года спустя): В настоящее время я использовал Timber для ведения журнала Android.
Не только это немного лучше, чем реализация по умолчанию Log
- тег журнала устанавливается автоматически, и легко записывать отформатированные строки и исключения - но вы также можете указать различные способы ведения журнала во время выполнения.
В этом примере заявления о регистрации будут записываться только в logcat в отладочных сборках моего приложения:
В моем методе Application
onCreate()
устанавливается Timber:
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
Тогда где-нибудь еще в моем коде я могу легко записывать:
Timber.d("Downloading URL: %s", url);
try {
// ...
} catch (IOException ioe) {
Timber.e(ioe, "Bad things happened!");
}
Обратитесь к Пример приложения для лесоматериалов для более продвинутого примера, где все отчеты журнала отправляются на logcat во время разработки и, в процессе производства, отладки операторы регистрируются, но сообщения об ошибках сообщаются в Crashlytics.
Ответ 2
Все хорошие ответы, но когда я закончил свою разработку, я не хотел использовать операторы if для всех вызовов Log, а также не хотел использовать внешние инструменты.
Поэтому решение, которое я использую, состоит в том, чтобы заменить класс android.util.Log собственным классом Log:
public class Log {
static final boolean LOG = BuildConfig.DEBUG;
public static void i(String tag, String string) {
if (LOG) android.util.Log.i(tag, string);
}
public static void e(String tag, String string) {
if (LOG) android.util.Log.e(tag, string);
}
public static void d(String tag, String string) {
if (LOG) android.util.Log.d(tag, string);
}
public static void v(String tag, String string) {
if (LOG) android.util.Log.v(tag, string);
}
public static void w(String tag, String string) {
if (LOG) android.util.Log.w(tag, string);
}
}
Единственное, что мне нужно было сделать во всех исходных файлах, это заменить импорт android.util.Log своим собственным классом.
Ответ 3
Я предлагаю иметь статическое булево значение, указывающее, следует ли записывать:
class MyDebug {
static final boolean LOG = true;
}
Затем, где бы вы ни захотели войти в свой код, просто выполните следующее:
if (MyDebug.LOG) {
if (condition) Log.i(...);
}
Теперь, когда вы установите MyDebug.LOG в false, компилятор вычеркнет весь код внутри таких проверок (так как это статический финал, он знает во время компиляции, что код не используется.)
Для больших проектов вы можете захотеть начать с булевых в отдельных файлах, чтобы иметь возможность легко включать или отключать ведение журнала там, где это необходимо. Например, это различные константы ведения журнала, которые у нас есть в диспетчере окон:
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
static final boolean DEBUG_FOCUS = false;
static final boolean DEBUG_ANIM = false;
static final boolean DEBUG_LAYOUT = false;
static final boolean DEBUG_RESIZE = false;
static final boolean DEBUG_LAYERS = false;
static final boolean DEBUG_INPUT = false;
static final boolean DEBUG_INPUT_METHOD = false;
static final boolean DEBUG_VISIBILITY = false;
static final boolean DEBUG_WINDOW_MOVEMENT = false;
static final boolean DEBUG_ORIENTATION = false;
static final boolean DEBUG_APP_TRANSITIONS = false;
static final boolean DEBUG_STARTING_WINDOW = false;
static final boolean DEBUG_REORDER = false;
static final boolean DEBUG_WALLPAPER = false;
static final boolean SHOW_TRANSACTIONS = false;
static final boolean HIDE_STACK_CRAWLS = true;
static final boolean MEASURE_LATENCY = false;
С соответствующим кодом типа:
if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
TAG, "Adding window " + window + " at "
+ (i+1) + " of " + mWindows.size() + " (after " + pos + ")");
Ответ 4
Решение Christopher Proguard является лучшим, но если по какой-либо причине вам не нравится Proguard, вот очень низкотехнологичное решение:
Журналы комментариев:
find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'
Неудовлетворительные журналы:
find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'
Ограничение состоит в том, что ваши команды ведения журнала не должны охватывать несколько строк.
(Выполняйте эти строки в оболочке UNIX в корне вашего проекта. Если вы используете Windows, получите уровень UNIX или используйте эквивалентные команды Windows)
Ответ 5
Я хотел бы добавить некоторые рекомендации об использовании Proguard с Android Studio и gradle, так как у меня было много проблем, чтобы удалить строки журнала из окончательного двоичного файла.
Чтобы сделать assumenosideeffects
в работах Proguard, есть предпосылка.
В вашем файле gradle вы должны указать использование proguard-android-optimize.txt
в качестве файла по умолчанию.
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// With the file below, it does not work!
//proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
Собственно, в файле proguard-android.txt
по умолчанию оптимизация отключается с помощью двух флагов:
-dontoptimize
-dontpreverify
Файл proguard-android-optimize.txt
не добавляет эти строки, поэтому теперь может работать assumenosideeffects
.
Тогда, лично, я использую SLF4J, тем более, когда я разрабатываю некоторые библиотеки, которые распространяются среди других. Преимущество состоит в том, что по умолчанию нет выхода. И если интегратор хочет получить некоторые выходные данные журнала, он может использовать Logback для Android и активировать журналы, поэтому журналы могут быть перенаправлены в файл или в LogCat.
Если мне действительно нужно разбить журналы из последней библиотеки, я затем добавлю в свой файл Proguard (после того, как, конечно, включил файл proguard-android-optimize.txt
):
-assumenosideeffects class * implements org.slf4j.Logger {
public *** trace(...);
public *** debug(...);
public *** info(...);
public *** warn(...);
public *** error(...);
}
Ответ 6
Я использовал класс LogUtils, как в примере приложения Google IO. Я изменил это, чтобы использовать константу DEBUG для конкретного приложения вместо BuildConfig.DEBUG, потому что BuildConfig.DEBUG ненадежна. Затем в моих классах у меня есть следующее.
import static my.app.util.LogUtils.makeLogTag;
import static my.app.util.LogUtils.LOGV;
public class MyActivity extends FragmentActivity {
private static final String TAG = makeLogTag(MyActivity.class);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LOGV(TAG, "my message");
}
}
Ответ 7
Я настоятельно рекомендую использовать Timber от Jake Wharton
https://github.com/JakeWharton/timber
он решает вашу проблему с включением/отключением плюс добавляет класс тега автоматически
просто
public class MyApp extends Application {
public void onCreate() {
super.onCreate();
//Timber
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
}
...
Журналы будут использоваться только в вашем debug ver, а затем используйте
Timber.d("lol");
или
Timber.i("lol says %s","lol");
для печати
"Ваш класс /msg " без указания тега
Ответ 8
Я бы рассмотрел использование roboguice средство ведения журнала вместо встроенного android.util.Log
Их средство автоматически отключает отладочные и подробные журналы для релизов.
Кроме того, вы бесплатно получаете отличные функции (например, настраиваемое поведение ведения журнала, дополнительные данные для каждого журнала и т.д.)
Использование proguard может быть довольно сложным, и я не стал бы испытывать проблемы с настройкой и сделать его работать с вашим приложением, если у вас нет веских оснований для этого (отключение журналов не является хороший)
Ответ 9
Я публикую это решение, которое применяется специально для пользователей Android Studio. Я также недавно обнаружил Timber и успешно импортировал его в свое приложение, выполнив следующие действия:
Поместите последнюю версию библиотеки в ваш build.gradle:
compile 'com.jakewharton.timber:timber:4.1.1'
Затем в Android Studios, перейдите в Edit → Find → Replace in Path...
Введите Log.e(TAG,
или же вы определили свои сообщения журнала в текстовое поле "Text to find"
. Затем просто замените его на Timber.e(
![enter image description here]()
Нажмите "Найти", а затем заменить все.
Android Studios теперь будет просматривать все ваши файлы в вашем проекте и заменять все журналы тембрами.
Единственная проблема, с которой я столкнулся при использовании этого метода, заключается в том, что gradle впоследствии выдает миллион сообщений об ошибках, потому что он не может найти "Timber" в импорте для каждого из ваших файлов Java. Просто нажмите на ошибки, и Android Studios автоматически импортирует "Timber" в вашу Java. Как только вы сделали это для всех ваших файлов ошибок, gradle снова скомпилирует.
Вам также необходимо поместить этот фрагмент кода в метод onCreate
вашего класса Application
:
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
Это приведет к регистрации приложения только тогда, когда вы находитесь в режиме разработки, а не в производстве. Вы также можете иметь BuildConfig.RELEASE
для входа в режиме выпуска.
Ответ 10
Per android.util.Log предоставляет способ включения/отключения журнала:
public static native boolean isLoggable(String tag, int level);
По умолчанию метод isLoggable (...) возвращает false, только после того, как вы установили propro в устройстве, понравилось это:
adb shell setprop log.tag.MyAppTag DEBUG
Это означает, что любой журнал выше уровня DEBUG может быть распечатан. Ссылка на андроид:
Проверяет, зарегистрирован ли журнал для указанного тега на указанном уровне. Уровень по умолчанию для любого тега установлен к ИНФО. Это означает, что любой уровень выше и включая INFO будет журнал. Прежде чем делать какие-либо вызовы методу ведения журнала, вы должны проверить чтобы проверить, должен ли ваш тег регистрироваться. Вы можете изменить уровень по умолчанию установив системное свойство: 'setprop log.tag. ' Где уровень - VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT или SUPPRESS. SUPPRESS отключит все записи для вашего тега. Ты можешь также создайте файл local.prop, который содержит в себе следующее: 'log.tag. =' и поместите это в /data/local.prop.
Таким образом, мы можем использовать пользовательский журнал:
public final class Dlog
{
public static void v(String tag, String msg)
{
if (Log.isLoggable(tag, Log.VERBOSE))
Log.v(tag, msg);
}
public static void d(String tag, String msg)
{
if (Log.isLoggable(tag, Log.DEBUG))
Log.d(tag, msg);
}
public static void i(String tag, String msg)
{
if (Log.isLoggable(tag, Log.INFO))
Log.i(tag, msg);
}
public static void w(String tag, String msg)
{
if (Log.isLoggable(tag, Log.WARN))
Log.w(tag, msg);
}
public static void e(String tag, String msg)
{
if (Log.isLoggable(tag, Log.ERROR))
Log.e(tag, msg);
}
}
Ответ 11
Если вы можете запустить глобальную замену (один раз), и после этого сохранить некоторые конвенции кодирования, вы можете следовать шаблону, часто используемый в Android рамки.
Вместо того чтобы писать
Log.d(TAG, string1 + string2 + arg3.toString());
иметь это как
if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());
Теперь proguard может удалить StringBuilder и все строки и методы, которые он использует в пути, из оптимизированного выпуска DEX. Используйте proguard-android-optimize.txt
и вам не нужно беспокоиться об android.util.Log в вашем proguard-rules.pro
:
android {
…
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
С плагином для Android Studio BuildConfig. DEBUG
BuildConfig. DEBUG
достаточно надежен, так что вам не нужны дополнительные константы для управления зачисткой.
Ответ 12
У меня очень простое решение. Я использую IntelliJ для разработки, поэтому детали меняются, но идея должна применяться во всех IDE.
Я выбираю корень моего исходного дерева, щелкните правой кнопкой мыши и выберите "заменить". Затем я выбираю заменить все "Log" . с "//Log.". Это удаляет все операторы журнала. Чтобы вернуть их позже, я повторяю ту же замену, но на этот раз заменяю все "//Log". с "Log" .
Хорошо работает для меня. Не забудьте установить замену как чувствительную к регистру, чтобы избежать несчастных случаев, таких как "Диалог". Для дополнительной гарантии вы также можете сделать первый шаг с помощью "Log" . как строку для поиска.
Brilliant.
Ответ 13
![введите описание изображения здесь]()
Это то, что я использовал для своих проектов Android.
В Android Studio мы можем выполнить аналогичную операцию с помощью Ctrl + Shift + F для поиска из всего проекта (Command + Shift + F в MacOs) и Ctrl + Shift + R для замены ((Command + Shift + R в MacOs) )
Ответ 14
Как комментарий zserge,
Древесина очень приятная, но если у вас уже есть существующий проект, вы можете попробовать github.com/zserge/log. Это замена для android.util.Log и большая часть функций, которые имеет Timber и даже больше.
его библиотека журналов предоставляет простой переключатель включения/отключения журнала, как показано ниже.
Кроме того, требуется только изменить строки import
, и ничего не нужно изменять для оператора Log.d(...);
.
if (!BuildConfig.DEBUG)
Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
Ответ 15
Добавьте следующий файл proguard-rules.txt
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** w(...);
public static *** v(...);
public static *** i(...);
}
Ответ 16
Я улучшил решение выше, предоставив поддержку разных уровней журналов и автоматически изменив уровни журналов в зависимости от того, выполняется ли код на реальном устройстве или на эмуляторе.
public class Log {
final static int WARN = 1;
final static int INFO = 2;
final static int DEBUG = 3;
final static int VERB = 4;
static int LOG_LEVEL;
static
{
if ("google_sdk".equals(Build.PRODUCT) || "sdk".equals(Build.PRODUCT)) {
LOG_LEVEL = VERB;
} else {
LOG_LEVEL = INFO;
}
}
/**
*Error
*/
public static void e(String tag, String string)
{
android.util.Log.e(tag, string);
}
/**
* Warn
*/
public static void w(String tag, String string)
{
android.util.Log.w(tag, string);
}
/**
* Info
*/
public static void i(String tag, String string)
{
if(LOG_LEVEL >= INFO)
{
android.util.Log.i(tag, string);
}
}
/**
* Debug
*/
public static void d(String tag, String string)
{
if(LOG_LEVEL >= DEBUG)
{
android.util.Log.d(tag, string);
}
}
/**
* Verbose
*/
public static void v(String tag, String string)
{
if(LOG_LEVEL >= VERB)
{
android.util.Log.v(tag, string);
}
}
}
Ответ 17
ProGuard сделает это для вас в вашей сборке релизов, и теперь хорошие новости от android.com:
http://developer.android.com/tools/help/proguard.html
Инструмент ProGuard сжимает, оптимизирует и обфускает ваш код, удаляя неиспользуемый код и переименовывая классы, поля и методы с семантически неясными именами. В результате получается меньший размер .apk файла, который сложнее перестроить. Поскольку ProGuard упрощает работу с вашим приложением, важно, чтобы вы использовали его, когда ваше приложение использует функции, чувствительные к безопасности, например, когда вы лицензируете свои приложения.
ProGuard интегрирован в систему сборки Android, поэтому вам не нужно ссылаться на нее вручную. ProGuard запускается только при создании вашего приложения в режиме выпуска, поэтому вам не нужно иметь дело с запутанным кодом при создании приложения в режиме отладки. Прогон ProGuard полностью необязателен, но рекомендуется.
В этом документе описывается, как включать и настраивать ProGuard, а также использовать инструмент retrace для декодирования обфускационных трасс стека
Ответ 18
Мне нравится использовать Log.d(TAG, некоторая строка, часто String.format()).
TAG всегда является именем класса
Transform Log.d(TAG, → Logd (в тексте вашего класса
private void Logd(String str){
if (MainClass.debug) Log.d(className, str);
}
Таким образом, когда вы готовы создать версию выпуска, установите для параметра MainClass.debug значение false!
Ответ 19
Журналы можно удалить с помощью bash в linux и sed:
find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'
Работает для многострочных журналов. В этом решении вы можете быть уверены, что в производственном коде журналы отсутствуют.
Ответ 20
Я знаю, что это старый вопрос, но почему вы не заменили все свои логические вызовы чем-то вроде
Boolean logCallWasHere = true;//--- остальная часть вашего журнала здесь
Вот почему вы узнаете, когда захотите вернуть их, и они не повлияют на ваш оператор if:)
Ответ 21
Почему бы просто не сделать
if(BuildConfig.DEBUG)
Log.d("tag","msg");
? Не нужно никаких дополнительных библиотек, никаких правил proguard, которые могут испортить проект, и java-компилятор просто пропустит байт-код для этого вызова, когда вы сделаете релизную сборку.
Ответ 22
самый простой способ:
использовать DebugLog
Все журналы отключены DebugLog при отпускании приложения.
https://github.com/MustafaFerhan/DebugLog