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

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

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

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

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

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

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

371
задан Nicolas Raoul 18 July 2019 в 08:37
поделиться

22 ответа

Я считаю, что гораздо более простое решение - забыть все проверки if повсеместно и просто использовать ProGuard , чтобы убрать любые вызовы методов 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 и платформу Android JAR вы используете.

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


Обновление (4,5 года спустя): В настоящее время я использовал Timber для регистрации в Android.

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

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

Timber настроен в моем методе 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!");
}

См. Пример приложения Timber для более продвинутого примера, где все операторы журнала отправляются в logcat во время разработки и, в производстве никакие отладочные операторы не регистрируются, но об ошибках молча сообщается Crashlytics.

460
ответ дан Christopher Orr 18 July 2019 в 08:37
поделиться

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

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

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

https://github.com/MustafaFerhan/DebugLog

-1
ответ дан Mustafa Ferhan 18 July 2019 в 08:37
поделиться

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

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

0
ответ дан masood elsad 18 July 2019 в 08:37
поделиться

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

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

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

0
ответ дан sylwano 18 July 2019 в 08:37
поделиться

Мне нравится использовать 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!

0
ответ дан user462990 18 July 2019 в 08:37
поделиться

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

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

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

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

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

1
ответ дан Max Gold 18 July 2019 в 08:37
поделиться

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

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


}
1
ответ дан danwms 18 July 2019 в 08:37
поделиться

Как в комментарии zserge ,

Timber очень хорош, но если у вас уже есть существующий проект - вы можете попробовать 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
2
ответ дан Community 18 July 2019 в 08:37
поделиться

enter image description here

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

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

3
ответ дан Lins Louis 18 July 2019 в 08:37
поделиться

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

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

Прекрасно работает для меня. Просто не забудьте установить замену как чувствительную к регистру, чтобы избежать несчастных случаев, таких как «Диалог». Для дополнительной уверенности вы также можете сделать первый шаг с «Журнал». в качестве строки для поиска.

Brilliant.

3
ответ дан kg_sYy 18 July 2019 в 08:37
поделиться

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

-assumenosideeffects class android.util.Log {
  public static *** d(...);
  public static *** w(...);
  public static *** v(...);
  public static *** i(...);
}
3
ответ дан eranga 18 July 2019 в 08:37
поделиться

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

6
ответ дан Simon 18 July 2019 в 08:37
поделиться

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

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

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

adb shell setprop log.tag.MyAppTag DEBUG

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

Проверяет, регистрируется ли журнал для указанного тега на указанном уровне. Уровень по умолчанию для любого тега установлен на INFO. Это означает, что любой уровень выше, включая INFO, будет зарегистрирован. Прежде чем делать какие-либо вызовы метода регистрации, вы должны проверить, должен ли ваш тег регистрироваться. Вы можете изменить уровень по умолчанию, установив системное свойство: 'setprop log.tag. «Где уровень - VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT или SUPPRESS. SUPPRESS отключит все записи для вашего тега. Вы также можете создать файл local.prop со следующим: «log.tag. =» И поместить его в /data/local.prop.

.

Таким образом, мы могли бы использовать custom 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);
    }
}
6
ответ дан Richard 18 July 2019 в 08:37
поделиться

Я использовал класс 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");
  }
}
8
ответ дан vman 18 July 2019 в 08:37
поделиться

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

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» без указания тега

8
ответ дан AndroidGecko 18 July 2019 в 08:37
поделиться

Я хотел бы добавить некоторые подробности об использовании 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(...);
}
16
ответ дан Yojimbo 18 July 2019 в 08:37
поделиться

Решение Кристофера Proguard является лучшим, но если по какой-либо причине вам не нравится Proguard, вот очень низкотехнологичное решение:

Журналы комментариев:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/Log\./;\/\/ Log\./g'

Раскомментировать logs:

find . -name "*\.java" | xargs grep -l 'Log\.' | xargs sed -i 's/;\/\/ Log\./Log\./g'

Ограничение состоит в том, что ваши инструкции по журналу не должны занимать несколько строк.

(Выполните эти строки в оболочке UNIX в корне вашего проекта. Если вы используете Windows, получите слой UNIX или используйте эквивалентные команды Windows)

30
ответ дан Nicolas Raoul 18 July 2019 в 08:37
поделиться

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

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 + ")");
60
ответ дан hackbod 18 July 2019 в 08:37
поделиться

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

Таким образом, решение, которое я использую, состоит в том, чтобы заменить класс android.util.Log своим собственным классом Log:

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.util.Log с моим собственным классом.

111
ответ дан Jorgesys 18 July 2019 в 08:37
поделиться

Если вы можете запустить глобальную замену (один раз) и после этого сохранить некоторое соглашение о кодировании, вы можете следовать шаблону, часто используемому в платформе 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'
    }
  }
}

С плагином gradle для Android Studio BuildConfig.DEBUG довольно надежный, поэтому вам не нужны дополнительные константы для контроля зачистки.

5
ответ дан Alex Cohn 18 July 2019 в 08:37
поделиться

Почему не просто делают

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

? Никаким дополнительным библиотекам не было нужно, никакие прозащитные правила, которые имеют тенденцию завинчивать проект, и компилятор Java просто не учтет байт-код для для этого вызова при создании сборки конечных версий.

0
ответ дан 22 November 2019 в 23:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: