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

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

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

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

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

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

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


person Nicolas Raoul    schedule 15.03.2010    source источник
comment
+1, потому что я не помнил, что это было в контрольном списке публикации.   -  person rds    schedule 22.07.2011
comment
Чтобы закомментировать неблокированную строку, я использую; // вместо //.   -  person yingted    schedule 06.01.2012
comment
Если вам нужно отменить это, вы, вероятно, захотите вместо этого использовать sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'.   -  person yingted    schedule 06.01.2012
comment
Возможный дубликат: stackoverflow.com/q/2018263/2291   -  person Jon Adams    schedule 25.09.2012
comment
Ссылка, которую добавил Димитар, больше не работает. Я нашел это вместо source.android.com/source/code-style. html # log-sparingly.   -  person JosephL    schedule 30.03.2013
comment
вот почему не рекомендуется использовать оператор if без {}, особенно когда вы перемещаете выражение на следующую строку; используйте Sonar Luke.   -  person Marian Paździoch    schedule 26.09.2014
comment
Есть ли какое-либо влияние на производительность, если ведение журнала включено или эта заметка предназначена только для целей безопасности?   -  person mboy    schedule 12.11.2015
comment
@mboy: Вероятно, в основном для производительности в настоящее время, но в старых версиях Android он также имеет преимущества безопасности.   -  person Nicolas Raoul    schedule 12.11.2015


Ответы (28)


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

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


Обновление (спустя 4,5 года): в настоящее время я использую Timber для ведения журнала 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!");
}

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

person Christopher Orr    schedule 17.03.2010
comment
Для Android правильно? Ваш проект с открытым исходным кодом? Могу я где-нибудь достать ваш Ant-файл? :-) Большое спасибо! - person Nicolas Raoul; 18.03.2010
comment
Я добавил базовый файл конфигурации, который вы можете использовать при вызове ProGuard из цели Ant. - person Christopher Orr; 18.03.2010
comment
И почему этого нет в файле proguard по умолчанию? - person rds; 22.07.2011
comment
+ rds, поскольку это приведет к тому, что номера строк производственной трассировки стека будут отличаться от тех, что указаны в вашем коде, поскольку строки будут удалены. - person Guy; 21.02.2012
comment
ProGuard работает с байт-кодом, а не с исходным кодом Java, поэтому номера строк не должны быть затронуты. Но если вы оптимизируете, номера строк все равно удаляются; тогда вам нужно сохранить с флагом -keepattributes. - person Christopher Orr; 22.02.2012
comment
Я могу подтвердить, что удаление вызовов журнала приведет к смещению номеров строк в трассировке стека. Это не всегда будет рассинхронизировано (я провел несколько быстрых тестов, но не могу точно определить причину, возможно, если вы объедините строку в вызове журнала), но иногда это будет несколько строк с ошибкой. ИМО стоит проблем из-за возможности легко удалять вызовы журнала. - person Tony Chan; 15.06.2012
comment
В ADT19 оптимизация по умолчанию отключена, поэтому -assumenosideeffects будет игнорироваться. Думаю, то же самое для ADT18 и 19. - person tidbeck; 04.07.2012
comment
Разве мы не можем просто отредактировать proguard.cfg в Eclipse и покончить с ним (нам, простым сборщикам Eclipse, не нужен Ant?). И что случилось с комментарием @tidebeck о том, что assumenosideeffects будут игнорироваться? - person Fraggle; 27.07.2012
comment
@Fraggle Из proguard-android.txt в инструментах ADT: обратите внимание, что если вы хотите включить оптимизацию, вы не можете просто включить флаги оптимизации в свой собственный файл конфигурации проекта; вместо этого вам нужно будет указать файл proguard-android-optimize.txt вместо этого из вашего файла # project.properties. - person Raanan; 07.12.2012
comment
Я добавил эти строки в proguard-project.txt и proguard.config = proguard-project.txt в project.properties, и с устройством, подключенным к ПК с открытым Eclipse, я всегда могу получить журналы. Это нормально? - person androniennn; 24.12.2012
comment
@androniennn Да. ProGuard запускается только в том случае, если вы выполняете сборку релиза своего приложения. - person Christopher Orr; 24.12.2012
comment
Разве это не ведет к ведению журнала в классах -keep, таких как те, которые расширяют android.app.Activity в этом примере? - person mparaz; 27.12.2012
comment
@mparaz Нет, лог снимается. - person Christopher Orr; 29.12.2012
comment
Это безопасно? Это работает, только если в конфигурации proguard я включаю оптимизацию, и - как говорит proguard-android-optimize.txt - некоторые оптимизации proguard не работают с некоторыми версиями Dalvik. - person user983447; 06.03.2013
comment
@ user983447 Да, хотя вам следует тщательно протестировать свое приложение после применения оптимизаций ProGuard. - person Christopher Orr; 06.03.2013
comment
@LucasTan Я задал именно этот вопрос здесь: stackoverflow.com/q/6009078/234938 - person Christopher Orr; 22.04.2014
comment
Любые указания о том, как настроить это в Android Studio? - person Rudey; 30.04.2014
comment
У меня это не работает. Журналы все еще отображаются, вероятно, из-за того, что оптимизация не включена. Может кто-нибудь объяснить, как его включить? В моем proguard-rules.txt есть такая строка: -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*, это часть проблемы? Кроме того, прав ли Раанан, что вы не должны включать их в свой собственный файл proguard? - person Micky; 20.08.2014
comment
Фу. Мне не нравится, что я должен звонить на Timber.tag перед каждым вызовом журнала. Вы что-то делаете, чтобы этого избежать? Или вы просто не пользуетесь тегами? - person Lo-Tan; 27.01.2015
comment
@ Lo-Tan Как я уже упоминал в ответе, Timber автоматически добавляет теги; Я никогда не использовал Timber.tag. - person Christopher Orr; 27.01.2015
comment
В eclipse конфигурация proguard работает должным образом, и ни один из журналов не отображается. Но в студии Android журналы все еще отображаются. Кто-нибудь еще сталкивается с той же проблемой.? - person Aditya Kamath; 09.02.2015
comment
он не работает ... все еще показывает все журналы после выпуска с помощью proguard. Я использовал класс журнала Android по умолчанию - person Gopal Singh Sirvi; 07.04.2016
comment
Хотя это старый пост, из любопытства, я думал, что, если в приложении есть неперехваченное исключение? Такие исключения будут регистрироваться в консоли, как избежать выброса неперехваченного исключения в консоль? - person Kaps; 21.04.2016
comment
@kaps Это не связано с этим вопросом, но Java позволяет вам зарегистрировать обработчик для перехвата необработанных в противном случае исключений. - person Christopher Orr; 21.04.2016
comment
Как сказал Эспинчи в ответе ниже. Единственная проблема с этим подходом заключается в том, что если вы выполняете Log.d (tag, Processed: + new ItemCounter (blabla) + items), даже если это сообщение журнала не появляется в вашей выпущенной версии, StringBuilder используется для создания сообщение, создание которого может быть дорогостоящим. Верно ли это и в случае с Timber? - person Chitrang; 17.05.2016
comment
@Chitrang Если вы правильно используете Timber (т. Е. Используя параметры формата), будет создана только одна константа String, а не StringBuilders. Любые методы, вызываемые в параметрах, не будут выполняться для сборок, в которых Timber не работает. Если вы действительно хотите убедиться, что строковые константы удалены из APK, я полагаю, вы могли бы использовать ProGuard с объявлением assumenosideeffects для Timber.x. - person Christopher Orr; 18.05.2016
comment
@ChristopherOrr Если дерево посажено в классе Application, есть ли необходимость в uproot его? Если да, не могли бы вы подсказать, где следует разместить корень? - person Zhi Kai; 01.12.2016
comment
Что, если мы случайно вышли из лога и опубликуем apk? это приводит к сбою приложения? - person Anus Kaleem; 05.03.2017
comment
Небольшое замечание, которое меня сбило с толку - компилятор Jack в настоящее время не поддерживает удаление журналов. См. stackoverflow.com/a/37942939/3035127 - person fattire; 08.03.2017
comment
Используйте BuildConfig.DEBUG, чтобы проверить, является ли сборка отладки рискованной, более надежный способ - использовать это: boolean isDebug = ((getContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); проверьте this для получения дополнительных сведений. - person gary; 09.02.2018
comment
@gary Это не имеет отношения к этому ответу, поскольку BuildConfig.DEBUG вызывается только один раз во время создания Application. - person Christopher Orr; 09.02.2018
comment
@ChristopherOrr Проблема с подходом proguard в том, что он оставляет строковые константы журналов в коде и упрощает обратный инжиниринг и вмешательство в ваш apk для тех, кто имеет такие намерения. Для меня также важно удалить строки из кода выпуска, и кажется, что это возможно только с помощью подхода if(DEBUG) {Log.d(...)}. - person Akram; 11.03.2018
comment
Использование библиотек типа timber: когда okhttp записывает данные; это все еще видно в logcat. - person MMK; 22.10.2020
comment
В случае с древесиной, если я использую несколько дорогостоящий вызов для печати в отладочном коде, такой как (kotlin) Timber.d("${printDebugOutputIteratingStuff()"), я полагаю, что древесина в любом случае не защитит меня от выполнения этого вызова, не так ли? - person Treviño; 10.01.2021

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

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

person Reiner    schedule 04.01.2011
comment
Единственная проблема с этим подходом заключается в том, что если вы выполняете Log.d (tag, Processed: + new ItemCounter (blabla) + items), даже если это сообщение журнала не появляется в вашей выпущенной версии, StringBuilder используется для создания сообщение, создание которого может быть дорогостоящим. - person espinchi; 07.09.2011
comment
У этого решения есть большая проблема. Эспинчи упомянул лишь верхушку айсберга. Проблема в том, что когда вы вызываете Log.d("tag", someValue.toString());, очень легко забыть проверить someValue на ненулевое значение, что означает, что он может выдать NullPointerException в процессе производства. Он предлагает безопасное решение, но вас обманет. Мы используем private static boolean DEBUG, а затем if(DEBUG)Log.d(TAG, msg); - person philipp; 08.08.2012
comment
Разве ProGuard (с настройками по умолчанию) не обнаружит эти пустые вызовы Log.d () как неиспользуемый код и не удалит их? - person Pavel Alexeev; 21.08.2012
comment
Да, Proguard их удаляет. Простая проверка: посмотрите, насколько разный размер APK с и без, потому что строки были удалены (при условии, что у вас много строк отладки) - person Philippe Girolami; 29.08.2012
comment
@espinchi Ваше беспокойство, похоже, относится ко всем библиотекам журналов, как описано в этом ответе stackoverflow.com/a/15452492/433718 ( Slf4j, отставание, ...). Разве их не предлагается использовать? - person OneWorld; 21.06.2013
comment
Чтобы ответить на мой собственный комментарий: при использовании этих библиотек операторы if должны использоваться также для проверки уровня журнала перед тем, как что-то регистрировать. См. Также заголовок stackoverflow.com/questions/4958860/ - person OneWorld; 21.06.2013
comment
Это решение хорошо, если вы не пользуетесь никакими библиотеками. но если да, вам придется изменить класс журнала во всех из них, если они с открытым исходным кодом и еще не скомпилированы. В основном, если вы используете любую банку, вы все равно увидите журнал от нее. - person code511788465541441; 25.10.2013
comment
Единственный способ минимизировать накладные расходы, упомянутые в 1-м комментарии @espinchi, - это изменить методы ведения журнала, чтобы они принимали varargs вместо String. Полное решение описано здесь. Очевидно, у этого есть еще один недостаток: нужно редактировать каждый вызов (а не только одну строку импорта). - person Stan; 27.11.2013
comment
Просто к сведению, если вы используете Android Studio и систему сборки gradle, вы можете использовать static final boolean LOG = BuildConfig.DEBUG и вам не нужно когда-либо изменять этот файл. - person ashishduh; 20.05.2014
comment
Еще одно преимущество, которое я вижу, вы можете спланировать другое дерево, которое будет регистрироваться в файле или кеше в памяти, и может совместно использовать эти журналы для целей отладки. - person Prakash; 04.08.2016
comment
@ashishduh, лучше private static final boolean IS_DEBUGGABLE = com.name.app.BuildConfig.DEBUG. - person CoolMind; 19.05.2017

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

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 + ")");
person hackbod    schedule 15.03.2010
comment
Я бы тоже проголосовал за такой подход. Он также использовался в официальном образце биллинга Google для приложений. - person LA_; 05.04.2011
comment
Разве не было бы менее подробным передать условие в качестве первого параметра? - person Snicolas; 31.08.2012
comment
Это кажется лучшим решением, хотя для каждого оператора журнала требуется дополнительный код: номера строк сохраняются (слабость подхода ProGuard), код для создания сообщения журнала не выполняется (слабость подхода класса оболочки и, очевидно, ведения журнала библиотечный подход также). Использование этого подхода в Google в примере биллинга приложений согласно @LA_ также поддерживает мои мысли. - person OneWorld; 21.06.2013
comment
@Snicolas Как передать условие в качестве первого параметра без реализации оболочки? Более того, если вы добавляете его как параметр, то перед входом в метод необходимо оценить все параметры, то есть также строку сообщения. Перед построением параметров необходимо проверить состояние. Предлагаемое решение, возможно, является лучшим без внешнего инструмента. - person type-a1pha; 21.07.2013
comment
@ type-a1pha ты прав. Теперь понимаю, что так оптимальнее. Тем не менее, я никогда не встречал библиотеки, которая точно соответствовала бы моим потребностям для ведения журнала на android. - person Snicolas; 21.07.2013
comment
Это лучше всего с точки зрения двоичного кода. Но такое кодирование требует больших усилий для простого вывода журнала отладки. Читаемость кода значительно падает. Выиграйте, немного проиграйте, я думаю ... - person Richard Le Mesurier; 12.12.2013
comment
Я знаю, что это старый ответ, но ради Линта я бы заменил LOG = true на LOG = Boolean.parseBoolean("true") - person Nick Cardoso; 11.05.2018

Решение 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)

person Nicolas Raoul    schedule 15.03.2010
comment
требуется после -i в Sed при работе на Mac (согласно this ) Спасибо. - person Vishal; 16.09.2012
comment
Я чувствую, что это может быть то, что я в конечном итоге использую для чего-то, над чем я работаю, потому что мне вообще не повезло с Proguard - person Joe Plante; 20.10.2012
comment
А что, если у вас есть журнал после ветки while без скобок, как вы предложили в своем первом посте? - person type-a1pha; 21.07.2013
comment
@ type-a1pha: если вы примете это решение, вы должны рассматривать блоки скобок как обязательные. - person Nicolas Raoul; 22.07.2013
comment
@NicolasRaoul Эта проблема решена с помощью точки с запятой (// против ;//) - person Alex Gittemeier; 02.08.2013
comment
где выполнить эти строчки в андроид студии? - person Gopal Singh Sirvi; 07.04.2016
comment
@GopalSinghSirvi: Я добавил последний абзац, объясняющий, как выполнять. Не в Android Studio. - person Nicolas Raoul; 07.04.2016
comment
На самом деле я пробую это в Android Studio .. Есть ли какой-либо метод для этого в Studio? - person Gopal Singh Sirvi; 07.04.2016
comment
@GopalSinghSirvi: Все вышеперечисленные решения достижимы в Android Studio, попробуйте их. - person Nicolas Raoul; 07.04.2016

Я хотел бы внести некоторые уточнения в использование 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(...);
}
person Vincent Hiribarren    schedule 09.09.2015
comment
Это не работает с новым компилятором Джека - stackoverflow.com/questions/37932114/ - person fattire; 08.03.2017
comment
Это мне помогло; и proguard-android-optimize.txt в качестве файла Proguard по умолчанию, и -assumenosideeffects в пользовательском файле Proguard были необходимы! Я использую shinker R8 (в настоящее время по умолчанию) и ведение журнала Android по умолчанию. - person Jonik; 28.04.2020

Я настоятельно рекомендую использовать 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());
    }
    ...

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

Timber.d("lol");

or

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

печатать

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

person AndroidGecko    schedule 20.10.2014

Я использовал 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");
  }
}
person JosephL    schedule 30.03.2013
comment
+1 за отчет об ошибке на Build.DEBUG, который я использовал. Я также отказался от различных правильных обходных путей и использую решение, похожее на ваше. - person Richard Le Mesurier; 12.12.2013

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

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

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

person Zvi    schedule 27.07.2011
comment
Очень хороший подход, когда вы не можете использовать Obfuscation .... особенно из-за взлома roboguice из-за proguard LOL - person Snicolas; 31.08.2012
comment
Обновленная ссылка на средство ведения журнала robojuice: github.com/roboguice/roboguice/wiki/Logging -via-Ln - person RenniePet; 21.10.2014

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

введите описание изображения здесь

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

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

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

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

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

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

person Simon    schedule 21.03.2016
comment
Попробуйте сделать то же самое для импорта и убедитесь, что в поле «Регулярное выражение» установлен флажок «Текст, чтобы найти: import android\.util\.Log\; Заменить на: import android\.util\.Log\;\nimport timber\.log\.Timber\;» - person Clark Wilson; 11.08.2016
comment
или вы можете использовать структурный поиск и заменить, как показывает Чике Мгбемена в его сообщение - person Maksim Turaev; 29.11.2016
comment
@MaksimTuraev Ваша ссылка больше не актуальна. Теперь это блог о прическах. - person Vadim Kotov; 05.02.2019
comment
Похоже, сообщение удалено = (нигде не могу найти. - person Maksim Turaev; 05.02.2019
comment
@MaksimTuraev вот копия с машины Wayback, но изображения битые - https://web.archive.org/web/20161004161318/http://chikemgbemena.com/2016/10/01/android-studio-structure-search-and-replace/ - person Vadim Kotov; 05.02.2019
comment
Заменить на: Timber.e(TAG + ": " + было бы лучше. - person DeveloperKurt; 04.06.2019

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

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

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

adb shell setprop log.tag.MyAppTag DEBUG

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

Проверяет, доступен ли журнал для указанного тега на указанном уровне. Уровень по умолчанию для любого тега установлен на INFO. Это означает, что будет регистрироваться любой уровень выше, включая ИНФОРМАЦИЮ. Перед тем, как делать какие-либо вызовы метода ведения журнала, вы должны проверить, должен ли ваш тег регистрироваться. Вы можете изменить уровень по умолчанию, установив системное свойство: 'setprop log.tag. 'Где level - это 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);
    }
}
person Richard    schedule 06.12.2013

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

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

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 gradle BuildConfig.DEBUG достаточно надежен, поэтому вам не нужны дополнительные константы для управления зачисткой.

person Alex Cohn    schedule 29.05.2017

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

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

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

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

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

Блестяще.

person kg_sYy    schedule 20.12.2014
comment
Пожалуйста, прочтите пункт Если я прокомментирую строку журнала в моем вопросе. - person Nicolas Raoul; 05.01.2015
comment
Хорошо, да, я должен перечитывать чаще после просмотра ответов :). Если у вас есть такие случаи, вам может потребоваться другое решение, например, предложенное ранее, например, размещение всех ваших журналов за другим интерфейсом. Мое предложение, возможно, лучше работает для небольших команд и проектов, где люди хотят избежать накладных расходов на дополнительные библиотеки журналирования, вы хорошо знаете людей и код и т. Д. - person kg_sYy; 06.01.2015
comment
Замена Log.d на; // Log.d позаботится и об этом сценарии If. - person Jasper; 11.07.2015

Как комментарий 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
person Youngjae    schedule 21.06.2016
comment
Вы должны помещать эту строку кода в каждое действие / фрагмент или просто в одно место? - person Noah Ternullo; 29.08.2016
comment
@NoahTernullo // в производном файле приложения. DefaultApplication.java - person Youngjae; 30.08.2016

введите здесь описание изображения

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

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

person Lins Louis    schedule 11.12.2015
comment
Кажется, это открывает работу с проектами eclipse. Опция поиска недоступна даже на студиях Android. - person Simon; 20.03.2016
comment
в Android Studio вы можете выполнить аналогичный поиск с помощью сочетания клавиш Ctrl + Shift + F - person Lins Louis; 21.03.2016
comment
Пример кода в вопросе объясняет, почему это ненадежно. - person Nicolas Raoul; 22.03.2016
comment
Это может вызвать проблемы при удалении любой команды, содержащейся в журнале. Например, chocolateLog.recipie (); - person Andrew S; 20.05.2016
comment
Невозможно найти эту опцию для Android Studio 2.1. Кроме того, я могу использовать этот трюк с 1 файлом за раз при обычном поиске / замене. - person VVB; 19.09.2016
comment
в Android Studio вы можете выполнить аналогичный поиск с помощью сочетания клавиш Ctrl + Shift + F и заменить на Ctrl + Shift + R - person Lins Louis; 20.09.2016

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

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


}
person danwms    schedule 29.03.2012
comment
Та же проблема, что и в предыдущем решении. Если строковый параметр создается с использованием дорогостоящих вызовов, он все равно тратит ресурсы. Перед построением переданных параметров необходимо выполнить проверку вызова. - person type-a1pha; 21.07.2013

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

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

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

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

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

person Max Gold    schedule 26.03.2013
comment
Однако, похоже, он не удаляет ведение журнала отладки по умолчанию. Так что ответ Кристофера звучит лучше. - person Nicolas Raoul; 26.03.2013

Вот как я решаю это в моем проекте Kotlin перед запуском в производство:

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

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int d(...);
    public static int w(...);
    public static int v(...);
    public static int i(...);
    public static int e(...);
}
person Emmanuel Ametepee    schedule 06.07.2020

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

TAG - это всегда имя класса

Преобразовать Log.d (TAG, -> Logd (в тексте вашего класса

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

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

person user462990    schedule 14.01.2015
comment
Проблема с этим и другими решениями, помимо proguard или их комментирования, заключается в том, что вы оставляете код, что, возможно, вызывает большое количество построений строк. в обычном приложении это не проблема, но если вы пытаетесь оптимизировать, это становится проблемой. - person Lassi Kinnunen; 20.04.2016

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

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

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

person sylwano    schedule 12.12.2016

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

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

person masood elsad    schedule 06.09.2017
comment
Интересно, что, надеюсь, такие строки игнорируются компилятором / оптимизатором. Однако имя переменной должно быть уникальным, потому что некоторые методы имеют несколько вызовов журнала, и вы не можете объявить одну и ту же переменную дважды. - person Nicolas Raoul; 07.09.2017
comment
Вы можете объявить переменную вверху активности и удалить логическое объявление из этой строки;) - person masood elsad; 08.09.2017

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

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

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

person c0dehunter    schedule 12.09.2019
comment
Неудобно то, что он более подробный, чем просто написание Log.d("tag","msg");, а также легко забыть о написании if(BuildConfig.DEBUG) части. - person Nicolas Raoul; 12.09.2019
comment
Еще одна проблема заключается в том, что строки остаются в упакованном релизе. - person straya; 10.10.2019

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

person Naci    schedule 29.02.2020

мой метод:

1) включить режим выбора столбца (alt + shift + insert)

2) выделить по одному Log.d (TAG, «текст»); часть «Журнал».

3) затем нажмите shift + ctrl + alt + j

4) щелкните стрелку влево

5) делать shift + end

6) нажмите удалить.

это удаляет сразу все вызовы LOG в файле java.

person Ronny Bigler    schedule 26.03.2020

С kotlin легко, просто объявите несколько функций верхнего уровня

val isDebug: Boolean
    get() = BuildConfig.DEBUG

fun logE(tag: String, message: String) {
    if (isDebug) Log.e(tag, message)
}

fun logD(tag: String, message: String) {
    if (isDebug) Log.d(tag, message)
}
person Zakhar Rodionov    schedule 08.05.2020

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

Итак, если вы не хотите ничего регистрировать в выпуске, просто реализуйте Logger, который ничего не делает, как в примере ниже:

import android.util.Log

sealed class Logger(defaultTag: String? = null) {
    protected val defaultTag: String = defaultTag ?: "[APP-DEBUG]"

    abstract fun log(string: String, tag: String = defaultTag)

    object LoggerDebug : Logger() {
        override fun log(string: String, tag: String) {
            Log.d(tag, string)
        }
    }

    object LoggerRelease : Logger() {
        override fun log(string: String, tag: String) {}
    }

    companion object {
        private val isDebugConfig = BuildConfig.DEBUG

        val instance: Logger by lazy {
            if(isDebugConfig)
            LoggerDebug
            else
                LoggerRelease
        }

    }
}

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

class MainActivity : AppCompatActivity() {

private val logger = Logger.instance

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    logger.log("Activity launched...")
    ...
    myView.setOnClickListener {
        ...

        logger.log("My View clicked!", "View-click")
    }
}

== ОБНОВЛЕНИЕ ==

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

// Add this function to the Logger class.
inline fun commit(block: Logger.() -> Unit) {
    if(this is LoggerDebug)
        block.invoke(this)
}

А потом:

 logger.commit {
     log("Logging without $myVar waste of resources"+ "My fancy concat")
 }

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

person Domenico    schedule 18.12.2020
comment
если вы сделаете Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), даже если это сообщение журнала не появляется в вашей выпущенной версии, StringBuilder используется для создания сообщения, создание которого может быть дорогостоящим. - person Nicolas Raoul; 21.12.2020
comment
Вы правы, в ситуации с кодом, критичным к производительности, создание конкатенации строк может быть дорогостоящим, особенно внутри циклов. В этих случаях я бы полностью удалил код регистрации с помощью PorGuard или каким-либо другим методом. В противном случае, если мы все еще хотим избежать конкатенации строк, но хотим решить проблему программно, мы можем использовать встроенный функциональный блок, который будет вызываться только в том случае, если мы находимся в конфигурации отладки. - person Domenico; 21.12.2020

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

используйте 1_

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

https://github.com/MustafaFerhan/DebugLog

person Mustafa Ferhan    schedule 01.07.2015
comment
Это абсолютно неверно. Это только приводит к тому, что журналы не регистрируются, но не удаляет их из кода, поэтому они все еще существуют, чтобы помогать людям реконструировать ваш код, и по-прежнему сопряжены с затратами на форматирование строк всех этих журналов. - person Glenn Maynard; 16.03.2016

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

Ctrl + Shift + R

заменять

Log.e(

С участием

// Log.e(
person Fthr    schedule 27.04.2020
comment
Это не будет работать с примером кода, приведенным в вопросе. - person Nicolas Raoul; 30.04.2020