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