Как удалить все вызовы журнала отладки перед публикацией приложения в Google Play? [закрытый]

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

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

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

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

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

Это официальный контрольный список, поэтому я думаю, что многие люди делают это на регулярной основе.
Итак, как эффективно, но безопасно удалить весь журнал линии?

21 ответов


Я нахожу гораздо более простое решение-забыть все if проверяет повсюду и просто использует должны быть чтобы удалить любой Log.d() или Log.v() метод вызывает, когда мы вызываем нашего муравья 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, передав только что скомпилированную банку и банку платформы Android, которую вы используете.

см. также примеры в руководстве ProGuard.


обновление (4.5 лет): сегодня я Лесной для ведения журнала Android.

не только это немного лучше, чем по умолчанию 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.утиль.Класс журнала с моим собственным классом журнала:

public class Log {
    static final boolean LOG = false;

    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.утиль.Войдите в мой собственный класс.


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

class MyDebug {
  static final boolean LOG = true;
}

тогда везде, где вы хотите войти в свой код, просто сделайте это:

if (MyDebug.LOG) {
  if (condition) Log.i(...);
}

теперь, когда вы устанавливаете MyDebug.Войдите в 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 или используйте эквивалентные окна команды)


Я хотел бы добавить некоторые уточнения об использовании 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 file, оптимизация отключена с помощью двух флаги:

-dontoptimize
-dontpreverify

на proguard-android-optimize.txt файл не добавляет эти строки, так что теперь assumenosideeffects может работать.

потом, лично я использую SLF4J, тем более, когда я разрабатываю некоторые библиотеки, которые распространяются на другие. Преимущество в том, что по умолчанию нет выхода. И если интегратор хочет некоторые выходы журнала, он может использовать Logback для Android и активировать журналы, поэтому журналы могут быть перенаправлены в файл или LogCat.

если мне действительно нужно зачищать журналы из окончательной библиотеки, затем я добавляю в свой файл Proguard (после включения конечно):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}

я использовал LogUtils класс, как в примере приложения 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.утиль.Log

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

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


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

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" без specyfing тега


на android.утиль.Log предоставляет способ включить / отключить log:

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

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

adb shell setprop log.tag.MyAppTag DEBUG

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

проверяет, является ли журнал для указанного тега loggable на указанном уровне. По умолчанию установлен уровень любого тега к информации. Это означает, что любой уровень выше и включая информацию будет отяжелевший. Перед вызовом метода ведения журнала необходимо проверить чтобы увидеть, должен ли ваш тег регистрироваться. Можно изменить уровень по умолчанию установив системное свойство: 'setprop log.метка. ' Где level-это VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT или ПОДАВЛЯТЬ. SUPPRESS отключит все ведение журнала для вашего тега. Вы можете также создайте локальный.prop файл, который со следующим в нем: - журнал.метка.= 'и поместите это в / data / local.прислонить.

поэтому мы могли бы использовать пользовательский журнал 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 Studio. Я также недавно обнаружил древесину и успешно импортировал ее в свое приложение, выполнив следующие действия:

поместите последнюю версию библиотеки в свою сборку.Gradle в:

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

затем в Android Studios перейдите в меню Правка - > найти - > заменить в пути...

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

enter image description here

Нажмите кнопку Найти, а затем заменить все.

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

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

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

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

это приведет к регистрации приложения только тогда, когда вы находитесь в режиме разработки не в производстве. Вы также можете иметь BuildConfig.RELEASE для входа в режим выхода.


если вы можете запустить глобальную замену (один раз), и после этого сохранить некоторое соглашение о кодировании, вы можете следовать шаблону, часто используемому в 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.утиль.Log в вашей proguard-rules.pro:

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

в студии плагин Gradle в Android, в BuildConfig.DEBUG довольно надежно, поэтому вам не нужны дополнительные константы для того чтобы контролировать обнажать.


enter image description here

Это то, что я использовал для своих проектов android..

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


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

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

отлично работает для меня. Просто не забудьте установить replace как case чувствительна, чтобы избежать несчастных случаев, таких как "диалог.". Для дополнительной гарантии, вы также можете сделать первый шаг с " Лог.- как строка для поиска.

блестящая.


As комментарий zserge предложил

древесина очень хорошая, но если у вас уже есть существующий проект - вы можете попробовать github.com/zserge/log - ... Это выпадающая замена для android.утиль.Журнал и имеет большинство функций, которые имеет древесина и даже больше.

его Библиотека журнала обеспечивает простой переключатель включения / выключения печати журнала, как показано ниже.

кроме того,только требует замены 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

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

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


добавьте в свой proguard-правила.txt

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

Мне нравится использовать Log.d (тег, некоторая строка, часто строка.формат.))(

тег всегда является именем класса

Преобразование Журнала.d (тег, -- > Logd (в тексте вашего класса

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

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


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

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

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


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

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


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

использовать DebugLog

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

https://github.com/MustafaFerhan/DebugLog