Я считаю, что гораздо более простое решение - забыть все проверки 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.
самый простой способ;
использовать DebugLog
Все журналы отключаются DebugLog при выпуске приложения.
Я знаю, что это старый вопрос, но почему вы не заменили все свои вызовы журнала чем-то вроде Boolean logCallWasHere = true; // --- остальная часть вашего журнала здесь
Вот почему вы будете знать, когда вы хотите вернуть их обратно, и они не повлияют на ваш вызов оператора if :):
Журналы можно удалить с помощью bash в linux и sed:
find . -name "*\.java" | xargs sed -ri ':a; s%Log\.[ivdwe].*\);%;%; ta; /Log\.[ivdwe]/ !b; N; ba'
Работает для многострочных журналов. В этом решении вы можете быть уверены, что журналы отсутствуют в рабочем коде.
Мне нравится использовать 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!
ProGuard сделает это за вас при сборке релиза, и теперь хорошие новости от android.com:
http://developer.android.com/tools/help/proguard.html
Инструмент ProGuard сжимает, оптимизирует и запутывает ваш код, удаляя неиспользуемый код и переименовывая классы, поля и методы с семантически скрытыми именами. В результате получается файл .apk меньшего размера, который сложнее проанализировать. Поскольку ProGuard усложняет обратное проектирование вашего приложения, важно использовать его, когда ваше приложение использует функции, чувствительные к безопасности, например, когда вы лицензируете свои приложения.
ProGuard интегрирован в систему сборки Android, поэтому вам не нужно вызывать его вручную. ProGuard запускается только при сборке приложения в режиме выпуска, поэтому вам не нужно иметь дело с запутанным кодом при сборке приложения в режиме отладки. Запуск ProGuard совершенно необязателен, но настоятельно рекомендуется.
В этом документе описывается, как включить и настроить ProGuard, а также использовать инструмент восстановления, чтобы декодировать запутанные следы стека.
Я улучшил вышеупомянутое решение, предоставляя поддержку различных уровней журнала и автоматически меняя уровни журнала в зависимости от того, выполняется ли код на работающем устройстве или на эмуляторе.
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);
}
}
}
Как в комментарии 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
Это то, что я делал на своих проектах для Android.
В Android Studio мы можем выполнить аналогичную операцию, нажав Ctrl + Shift + F для поиска по всему проекту (Command + Shift + F в MacOs) и Ctrl + Shift + R для замены ((Command + Shift + R в MacO) )
У меня очень простое решение. Я использую IntelliJ для разработки, поэтому детали меняются, но идея должна применяться ко всем IDE.
Я выбираю корень моего исходного дерева, щелкаю правой кнопкой мыши и выбираю «заменить». Я тогда решил заменить все "Журнал". с "// Журнал." Это удаляет все записи журнала. Чтобы вернуть их позже, я повторяю ту же самую замену, но на этот раз, чтобы заменить все "// Журнал". с «Журналом».
Прекрасно работает для меня. Просто не забудьте установить замену как чувствительную к регистру, чтобы избежать несчастных случаев, таких как «Диалог». Для дополнительной уверенности вы также можете сделать первый шаг с «Журнал». в качестве строки для поиска.
Brilliant.
Добавьте следующее в ваш файл proguard-rules.txt
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** w(...);
public static *** v(...);
public static *** i(...);
}
Я публикую это решение, которое применяется специально для пользователей 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 теперь будет просматривать все ваши файлы в вашем проекте и заменять все журналы тембрами.
Единственная проблема, с которой я столкнулся при использовании этого метода, заключается в том, что gradle впоследствии выдает миллион сообщений об ошибках, потому что не может найти «Timber» в импорте для каждого из ваших java-файлов. Просто нажмите на ошибки, и Android Studios автоматически импортирует «Timber» в вашу Java. Как только вы сделаете это для всех ваших файлов ошибок, gradle снова скомпилирует.
Вам также нужно поместить этот фрагмент кода в метод onCreate
вашего класса Application
:
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
Это приведет к регистрации приложения только тогда, когда вы находитесь в режиме разработки, а не в производство. Вы также можете иметь BuildConfig.RELEASE
для входа в режиме выпуска.
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);
}
}
Я хотел бы рассмотреть возможность использования средства регистрации roboguice вместо встроенного android.util.Log
Их средство автоматически отключает отладочные и подробные журналы для сборок релизов. Кроме того, вы бесплатно получаете некоторые изящные функции (например, настраиваемое поведение ведения журнала, дополнительные данные для каждого журнала и многое другое). работайте с вашим приложением, если у вас нет веских причин для этого (отключение журналов не является хорошим)
Я использовал класс 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");
}
}
Я настоятельно рекомендую использовать 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» без указания тега
Я хотел бы добавить некоторые подробности об использовании 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(...);
}
Решение Кристофера 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)
Я предлагаю иметь где-то статическое логическое значение, указывающее, регистрировать ли или нет:
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 + ")");
Все хорошие ответы, но когда я закончил свою разработку, я не хотел ни использовать операторы во всех вызовах журнала, ни использовать внешние инструменты.
Таким образом, решение, которое я использую, состоит в том, чтобы заменить класс 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 с моим собственным классом.
Если вы можете запустить глобальную замену (один раз) и после этого сохранить некоторое соглашение о кодировании, вы можете следовать шаблону, часто используемому в платформе 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
довольно надежный, поэтому вам не нужны дополнительные константы для контроля зачистки.
Почему не просто делают
if(BuildConfig.DEBUG)
Log.d("tag","msg");
? Никаким дополнительным библиотекам не было нужно, никакие прозащитные правила, которые имеют тенденцию завинчивать проект, и компилятор Java просто не учтет байт-код для для этого вызова при создании сборки конечных версий.