Как удалить все вызовы ведения журнала отладки перед созданием релизной версии приложения для Android?

StackOverflow https://stackoverflow.com/questions/2446248

Вопрос

Согласно Google, я должен "деактивируйте все вызовы методов журнала в исходном коде" перед публикацией моего приложения для Android в Google Play.Выдержка из раздела 3 закона контрольный список публикаций:

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

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

if(condition)
  Log.d(LOG_TAG, "Something");
data.load();
data.show();

Если я прокомментирую строку журнала, то условие применяется к следующей строке, и, скорее всего, load() не вызывается.Достаточно ли редки такие ситуации, чтобы я мог решить, что этого не должно существовать?

Итак, есть ли лучший способ сделать это на уровне исходного кода?Или, может быть, какой-нибудь умный синтаксис ProGuard для эффективного, но безопасного удаления всех строк журнала?

Это было полезно?

Решение

Я нахожу гораздо более простое решение - забыть все if проверяет повсюду и просто использует Предусмотрительный чтобы убрать какие-либо Log.d() или Log.v() метод вызывается, когда мы вызываем наш Ant release цель.

Таким образом, у нас всегда есть отладочная информация, выводимая для обычных сборок, и нам не нужно вносить какие-либо изменения в код для релизных сборок.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 года спустя): В настоящее время я использовал Древесина для ведения журнала Android.

Мало того, что это немного приятнее, чем по умолчанию Log реализация — тег log устанавливается автоматически, и легко регистрировать форматированные строки и исключения, но вы также можете указать другое поведение ведения журнала во время выполнения.

В этом примере инструкции ведения журнала будут записываться в logcat только в отладочных сборках моего приложения:

Древесина установлена в моем Application onCreate() способ:

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.

Другие советы

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

Итак, решение, которое я использую, - заменить класс 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 моим собственным классом.

Я предлагаю иметь где-нибудь статическое логическое значение, указывающее, регистрироваться или нет:

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 + ")");

Решение Кристофера 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)

Я хотел бы добавить некоторые уточнения об использовании Proguard с Android Studio и gradle, поскольку у меня было много проблем с удалением строк журнала из конечного двоичного файла.

Для того, чтобы сделать assumenosideeffects в Proguard works есть обязательное условие.

В вашем файле 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(...);
}

Я настоятельно рекомендую использовать Древесину от Джейка Уортона

https://github.com/JakeWharton/timber

это решает вашу проблему с включением / отключением, а также автоматически добавляет класс тегов

просто

public class MyApp extends Application {

  public void onCreate() {
    super.onCreate();
    //Timber
    if (BuildConfig.DEBUG) {
      Timber.plant(new DebugTree());
    }
    ...

журналы будут использоваться только в вашей отладочной версии, а затем используйте

Timber.d("lol");

или

Timber.i("lol says %s","lol");

для печати

"Ваш класс / msg" без указания тега

Я использовал Логические модули класс, как в примере приложения Google IO.Я изменил это, чтобы использовать константу отладки для конкретного приложения вместо BuildConfig.ОТЛАЖИВАТЬ, потому что BuildConfig.ОТЛАДКА ненадежна.Затем на моих Занятиях у меня происходит следующее.

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

Я бы подумал об использовании roboguice лесозаготовительный комплекс вместо встроенного android.util.Log

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

Использование proguard может быть довольно хлопотным делом, и я бы не стал утруждать себя его настройкой и созданием работа с вашим приложением, если у вас нет на то веских причин (отключение журналов не является хорошим решением).

Я публикую это решение, которое применимо специально для пользователей Android Studio.Я также недавно обнаружил Timber и успешно импортировал его в свое приложение, выполнив следующие действия:

Поместите последнюю версию библиотеки в свой build.gradle:

compile 'com.jakewharton.timber:timber:4.1.1'

Затем в Android Studios перейдите в меню Редактировать -> Найти -> Заменить в Path...

Введите в Log.e(TAG, или, однако, вы определили свои сообщения журнала в "Text to find" текстовое поле.Затем вы просто заменяете его на Timber.e(

enter image description here

Нажмите Найти, а затем заменить все.

Android Studios теперь просмотрит все ваши файлы в вашем проекте и заменит все журналы Timbers.

Единственная проблема, с которой я столкнулся с этим методом, заключается в том, что gradle впоследствии выдает миллион сообщений об ошибках, потому что он не может найти "Timber" в импорте для каждого из ваших java-файлов.Просто нажмите на ошибки, и Android Studios автоматически импортирует "Timber" в вашу java.Как только вы сделаете это для всех ваших файлов ошибок, gradle снова скомпилируется.

Вам также нужно поместить этот фрагмент кода в свой onCreate метод вашего Application класс:

    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    }

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

Для Android.util.Log предоставляет способ включить / отключить ведение журнала:

public static native boolean isLoggable(String tag, int level);

По умолчанию метод isLoggable(...) возвращает false, только после того, как вам понравится setprop в устройстве:

adb shell setprop log.tag.MyAppTag DEBUG

Это означает, что любой журнал выше уровня отладки может быть распечатан.Справочный документ для Android:

Проверяет, доступен ли журнал для указанного тега для ведения журнала на указанном уровне.Уровень по умолчанию для любого тега установлен на INFO.Это означает, что любой уровень выше и включая ИНФОРМАЦИЮ, будет зарегистрирован.Прежде чем выполнять какие-либо вызовы метода ведения журнала, вы должны проверить следует ли регистрировать ваш тег.Вы можете изменить уровень по умолчанию установив системное свойство:'setprop log.тег.' Где уровень либо ПОДРОБНЫЙ, ОТЛАДОЧНЫЙ, ИНФОРМАЦИОННЫЙ, ПРЕДУПРЕЖДАЮЩИЙ, ОШИБОЧНЫЙ, УТВЕРЖДАЮЩИЙ, либо ПОДАВЛЯТЬ.ПОДАВЛЕНИЕ отключит все протоколирование для вашего тега.Вы можете также создать локальный файл.prop, содержащий в себе следующее:'log.tag.=' и поместите это в /data/local.prop.

Таким образом, мы могли бы использовать пользовательский log util:

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

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

Вместо того, чтобы писать

Log.d(TAG, string1 + string2 + arg3.toString());

имейте это как

if (BuildConfig.DEBUG) Log.d(TAG, string1 + String.format("%.2f", arg2) + arg3.toString());

Теперь proguard может удалить StringBuilder и все строки и методы, которые он использует по пути, из оптимизированного release DEX.Использование proguard-android-optimize.txt и тебе не нужно беспокоиться о android.утилита.Log в вашем proguard-rules.pro:

android {
  …
  buildTypes {
    release {
      minifyEnabled true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }
}

С плагином Android Studio gradle, BuildConfig.ОТЛАЖИВАТЬ является достаточно надежным, поэтому вам не нужны дополнительные константы для управления зачисткой.

У меня есть очень простое решение.Я использую IntelliJ для разработки, поэтому детали различаются, но идея должна применяться ко всем IDE.

Я выбираю корень моего исходного дерева, щелкаю правой кнопкой мыши и выбираю "заменить".Затем я решаю заменить все "Log." на "//Log.".Это удаляет все инструкции журнала.Чтобы вернуть их позже, я повторяю ту же замену, но на этот раз заменяю все "//Log." на "Log.".

У меня это работает просто отлично.Просто не забудьте установить для замены значение с учетом регистра, чтобы избежать таких случайностей, как "Диалоговое окно"..Для дополнительной уверенности вы также можете выполнить первый шаг с помощью "Log." в качестве строки для поиска.

Блестяще.

Как комментарий 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

Добавьте следующее к вашему proguard-rules.txt файл

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}

enter image description here

Это то, что я раньше делал в своих проектах для Android..

В Android Studio мы можем выполнить аналогичную операцию с помощью Ctrl + Shift + F для поиска по всему проекту (Command + Shift + F в macOS) и Ctrl + Shift + R для замены ((Command + Shift + R в macOS))

Я улучшил описанное выше решение, предоставив поддержку различных уровней ведения журнала и автоматически изменив уровни ведения журнала в зависимости от того, выполняется ли код на живом устройстве или на эмуляторе.

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


}

ProGuard сделает это за вас при сборке вашего релиза, а теперь хорошие новости от android.com:

http://developer.android.com/tools/help/proguard.html

Инструмент ProGuard сокращает, оптимизирует и запутывает ваш код, удаляя неиспользуемый код и переименовывая классы, поля и методы с семантически неясными именами.В результате получается файл .apk меньшего размера, который сложнее перепроектировать.Поскольку ProGuard затрудняет обратное проектирование вашего приложения, важно, чтобы вы использовали его, когда ваше приложение использует функции, чувствительные к безопасности, например, когда вы лицензируете свои приложения.

ProGuard интегрирован в систему сборки Android, поэтому вам не нужно вызывать его вручную.ProGuard запускается только тогда, когда вы создаете свое приложение в режиме выпуска, поэтому вам не придется иметь дело с запутанным кодом при сборке приложения в режиме отладки.Запуск ProGuard совершенно необязателен, но настоятельно рекомендуется.

В этом документе описывается, как включить и настроить ProGuard, а также использовать инструмент retrace для декодирования запутанных трассировок стека

Мне нравится использовать Log.d(ТЕГ, некоторая строка, часто String.format ()).

ТЕГ - это всегда имя класса

Преобразуйте Log.d(ТЕГ, --> Logd( в текст вашего класса

private void Logd(String str){
    if (MainClass.debug) Log.d(className, str);
}

Таким образом, когда вы будете готовы создать релизную версию, установите MainClass.debug равным false!

Журналы могут быть удалены с помощью bash в Linux и sed:

find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'

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

Я знаю, что это старый вопрос, но почему вы не заменили все свои вызовы журнала чем-то вроде Логическое значение logCallWasHere=true;//---остальная часть вашего журнала здесь

Вот почему вы будете знать, когда захотите вернуть их обратно, и они не повлияют на ваш вызов оператора if :)

Почему бы просто не сделать

if(BuildConfig.DEBUG)
  Log.d("tag","msg");

?Никаких дополнительных библиотек не требуется, никаких правил proguard, которые, как правило, портят проект, а компилятор java просто пропустит байт-код для этого вызова при выполнении сборки релиза.

самый простой способ;

использование DebugLog

Все журналы отключаются DebugLog при выпуске приложения.

https://github.com/MustafaFerhan/DebugLog

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top