Jak usunąć wszystkie wywołania rejestrowania debugowania przed zbudowaniem wersji wydania aplikacji na Androida?


397

Według Google muszę „ dezaktywować wszelkie wywołania metod rejestrowania w kodzie źródłowym ” przed opublikowaniem mojej aplikacji na Androida w Google Play. Wyciąg z sekcji 3 listy kontrolnej publikacji :

Przed skompilowaniem aplikacji do wydania należy wyłączyć rejestrowanie i opcję debugowania. Można dezaktywować rejestrowanie, usuwając wywołania metod rejestrowania w plikach źródłowych.

Mój projekt typu open source jest duży i trudno go robić ręcznie za każdym razem, gdy go wypuszczam. Ponadto usunięcie wiersza dziennika jest potencjalnie trudne, na przykład:

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

Jeśli komentuję wiersz dziennika, wówczas warunek dotyczy następnego wiersza, a szanse, że load () nie zostaną wywołane. Czy takie sytuacje są na tyle rzadkie, że mogę zdecydować, że nie powinny istnieć?

Czy istnieje lepszy sposób na zrobienie tego na poziomie kodu źródłowego? A może sprytna składnia ProGuard, aby skutecznie, ale bezpiecznie usuwać wszystkie wiersze dziennika?


2
+1, ponieważ nie pamiętam, żeby było na liście kontrolnej publikacji.
rds

51
Aby skomentować niezablokowaną linię, używam „; //” zamiast „//”.
Yingted

Jeśli musisz to cofnąć, prawdopodobnie będziesz chciał użyć sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'zamiast tego.
odezwał się

2
Link dodany przez Dimitar już nie działa. Znalazłem to zamiast tego source.android.com/source/code-style.html#log-sparingly .
JosephL

1
@mboy: Prawdopodobnie obecnie głównie pod względem wydajności, ale w starszych wersjach Androida ma również zalety bezpieczeństwa.
Nicolas Raoul,

Odpowiedzi:


488

Uważam, że o wiele łatwiejszym rozwiązaniem jest zapomnienie wszystkich ifkontroli w dowolnym miejscu i po prostu skorzystanie z ProGuard, aby usunąć wszelkie wywołania metod Log.d()lub Log.v()metody, gdy wywołujemy nasz releasecel Ant .

W ten sposób zawsze otrzymujemy informacje debugowania dla regularnych kompilacji i nie musimy wprowadzać żadnych zmian w kodzie dla kompilacji wersji. ProGuard może również wykonywać wiele przejść przez kod bajtowy w celu usunięcia innych niepożądanych instrukcji, pustych bloków i może automatycznie wstawiać krótkie metody, jeśli to konieczne.

Na przykład, oto bardzo podstawowa konfiguracja ProGuard dla Androida:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

-keep class * extends android.app.Activity
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
}

Więc zapisz to w pliku, a następnie zadzwoń do ProGuard z Anta, przekazując właśnie skompilowany plik JAR i używany plik JAR platformy Android.

Zobacz także przykłady w podręczniku ProGuard.


Aktualizacja (4,5 roku później): Obecnie korzystam z rejestrowania drewna w systemie Android.

Jest nie tylko nieco ładniejszy niż domyślna Logimplementacja - znacznik dziennika jest ustawiany automatycznie, a także łatwo rejestrować sformatowane ciągi i wyjątki - ale można także określić różne zachowania rejestrowania w czasie wykonywania.

W tym przykładzie instrukcje rejestrowania zostaną zapisane do logcat tylko w kompilacjach debugowania mojej aplikacji:

W mojej Application onCreate()metodzie skonfigurowano drewno :

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

Następnie gdziekolwiek indziej w moim kodzie mogę się łatwo zalogować:

Timber.d("Downloading URL: %s", url);
try {
  // ...
} catch (IOException ioe) {
  Timber.e(ioe, "Bad things happened!");
}

Zobacz przykładową aplikację Timber, aby uzyskać bardziej zaawansowany przykład, w którym wszystkie instrukcje dziennika są wysyłane do logcat podczas programowania, a podczas produkcji nie są rejestrowane żadne instrukcje debugowania, ale błędy są bezgłośnie zgłaszane Crashlytics.


59
I dlaczego nie ma tego w domyślnym pliku proguard?
rds

10
+ rds, ponieważ spowoduje to, że linie wiersza stosu produkcji będą inne niż te w kodzie, gdy linie zostaną usunięte.
Guy

5
Mogę potwierdzić, że usunięcie logów spowoduje przesunięcie numerów linii w stosach. Nie zawsze będzie niezsynchronizowany (przeszedłem kilka szybkich testów, ale nie mogę dokładnie ustalić, co jest przyczyną, być może, jeśli połączysz ciąg znaków w wywołaniu dziennika), ale czasami będzie to kilka linii. Warto kłopot IMO za możliwość łatwego usuwania połączeń dziennika.
Tony Chan,

5
@Fraggle Z proguard-android.txt w narzędziach ADT: „Pamiętaj, że jeśli chcesz włączyć optymalizację, nie możesz po prostu dołączyć flag optymalizacji do własnego pliku konfiguracyjnego projektu; zamiast tego musisz wskazać„ optymalizacja proguard-android. txt ”zamiast tego z pliku„ # project.properties.
Raanan,

3
Jak powiedział espinchi w odpowiedzi poniżej. „Jedynym problemem związanym z tym podejściem jest to, że jeśli wykonasz Log.d („ tag ”,„ Przetwarzane: ”+ nowy ItemCounter (blabla) +„ items ”), nawet jeśli ten komunikat dziennika nie pojawia się w wydanej wersji, StringBuilder służy do tworzenia wiadomości, których utworzenie może być kosztowne. ”Czy to samo dotyczy przypadku Timbera?
Chitrang

117

Wszystkie dobre odpowiedzi, ale kiedy skończyłem mój rozwój, nie chciałem używać instrukcji if w obrębie wszystkich wywołań dziennika, ani nie chciałem używać narzędzi zewnętrznych.

Rozwiązaniem, którego używam, jest zastąpienie klasy android.util.Log własną klasą 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);
    }
}

Jedyną rzeczą, którą musiałem zrobić we wszystkich plikach źródłowych, było zastąpienie importu android.util.Log własną klasą.


143
Jedynym problemem związanym z tym podejściem jest to, że jeśli wykonasz Log.d („tag”, „Przetwarzane:” + nowy ItemCounter (blabla) + „items”), nawet jeśli ten komunikat dziennika nie pojawia się w wydanej wersji, a StringBuilder służy do tworzenia wiadomości, których utworzenie może być kosztowne.
espinchi,

9
To rozwiązanie ma duży problem. espinchi wspomniał tylko wierzchołek góry lodowej. Problem polega na tym, że kiedy dzwonisz Log.d("tag", someValue.toString());, bardzo łatwo jest zapomnieć o sprawdzeniu someValue pod kątem braku wartości zerowej, co oznacza, że ​​może to spowodować wzrost NullPointerExceptionprodukcji. To sugeruje bezpieczne rozwiązanie, ale oszuka cię. My, private static boolean DEBUGa potemif(DEBUG)Log.d(TAG, msg);
philipp

2
@espinchi Twoje obawy wydają się dotyczyć wszystkich bibliotek rejestrujących, takich jak omówione w tej odpowiedzi stackoverflow.com/a/15452492/433718 (Slf4j, zaległości, ...). Czy nie zaleca się ich używania?
OneWorld,

1
Jedynym sposobem na zminimalizowanie kosztów ogólnych wspomnianych w pierwszym komentarzu @espinchi jest zmiana metod rejestrowania w celu akceptowania varargs zamiast String. Kompletne rozwiązanie jest tutaj opisane . Ma to oczywiście inną wadę: każde połączenie powinno być edytowane (nie tylko jedna linia importu).
Stan

21
Tylko dla Ciebie, jeśli korzystasz z Android Studio i systemu kompilacji stopni, możesz użyć static final boolean LOG = BuildConfig.DEBUGtego pliku i nie musisz go modyfikować.
ashishduh

61

Proponuję mieć gdzieś statyczną wartość logiczną wskazującą, czy należy się zalogować:

klasa MyDebug {
  statyczny końcowy logiczny LOG = true;
}

Następnie, gdziekolwiek chcesz się zalogować, po prostu zrób to:

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

Teraz, gdy ustawisz MyDebug.LOG na false, kompilator usunie cały kod z takich kontroli (ponieważ jest to statyczny finał, wie, że w czasie kompilacji kod nie jest używany).

W przypadku większych projektów możesz chcieć zacząć mieć logiczne wartości w poszczególnych plikach, aby móc łatwo włączyć lub wyłączyć rejestrowanie w razie potrzeby. Na przykład są to różne stałe rejestrowania, które mamy w menedżerze okien:

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;

Z odpowiednim kodem, takim jak:

    if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v(
        TAG, "Adding window " + window + " at "
        + (i+1) + " of " + mWindows.size() + " (after " + pos + ")");

1
Głosowałbym również za takim podejściem. Wykorzystano go także w oficjalnej próbce rozliczeniowej Google w aplikacji.
LA_

4
Czy nie byłoby mniej gadatliwe przekazywanie warunku jako pierwszego parametru?
Snicolas,

1
Wydaje się, że jest to najlepsze rozwiązanie, chociaż wymaga dodatkowego kodu do każdej instrukcji dziennika: Numery linii są zachowane (słabość podejścia ProGuard), Nie jest wykonywany żaden kod do utworzenia komunikatu dziennika ( słabość podejścia klasy opakowania i najwyraźniej podejścia biblioteki dziennika również) . Zastosowanie tego podejścia w Google w próbce rozliczeń aplikacji według @LA_ również popiera moje przemyślenia.
OneWorld,

2
@Snicolas Jak przekazać warunek jako pierwszy parametr bez implementacji opakowania? Ponadto, jeśli dodasz go jako parametr, to przed wejściem do metody należy ocenić wszystkie parametry, czyli również ciąg komunikatu. Warunek należy przetestować przed zbudowaniem parametrów. Proponowane rozwiązanie jest prawdopodobnie najlepsze, ponieważ nie ma zewnętrznego narzędzia.
type-a1pha

2
Pod względem kodu binarnego jest to najlepsze. Ale kodowanie w ten sposób to tylko duży wysiłek w celu uzyskania prostego wyjścia z dziennika debugowania. Czytelność kodu znacznie spada. Wygraj trochę, trochę stracę, jak sądzę ...
Richard Le Mesurier,

30

Rozwiązanie Christopher's Proguard jest najlepsze, ale jeśli z jakiegokolwiek powodu nie podoba ci się Proguard, oto bardzo mało zaawansowane rozwiązanie:

Dzienniki komentarzy:

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

Dzienniki odkomentowania:

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

Ograniczeniem jest to, że instrukcje rejestrowania nie mogą obejmować wielu linii.

(Wykonaj te wiersze w powłoce UNIX w katalogu głównym projektu. Jeśli używasz systemu Windows, uzyskaj warstwę UNIX lub użyj równoważnych poleceń systemu Windows)


1
potrzebuję „” po -i w Sed, jeśli działa na Macu (zgodnie z tym ) Dzięki.
Vishal,

Wydaje mi się, że to może być to, czego ostatecznie używam do czegoś, nad czym pracuję, ponieważ w ogóle nie miałem szczęścia, robiąc to z Proguardem
Joe Plante

A co, jeśli masz dziennik po nieokreślonej gałęzi while, jak zasugerowałeś w swoim pierwszym poście?
type-a1pha

@ type-a1pha: Jeśli zastosujesz to rozwiązanie, musisz uważać bloki wsporników za obowiązkowe.
Nicolas Raoul,

2
@NicolasRaoul Średnik dwukropek rozwiązuje ten problem ( //vs. ;//)
Alex Gittemeier

18

Chciałbym dodać pewne szczegóły dotyczące używania Proguard z Android Studio i gradle, ponieważ miałem wiele problemów z usunięciem linii logów z ostatecznego pliku binarnego.

Warunkiem wykonania assumenosideeffectsprac Proguard jest spełnienie warunku wstępnego.

W pliku ocen musisz określić użycie proguard-android-optimize.txtpliku domyślnego.

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'
    }
}

W rzeczywistości w proguard-android.txtpliku domyślnym optymalizacja jest wyłączona z dwiema flagami:

-dontoptimize
-dontpreverify

proguard-android-optimize.txtPlik nie dodawać tych linii, więc teraz assumenosideeffectsmoże pracować.

Następnie osobiście używam SLF4J , tym bardziej, gdy rozwijam niektóre biblioteki, które są dystrybuowane do innych. Zaletą jest to, że domyślnie nie ma wyjścia. A jeśli integrator chce wyjść z dziennika, może użyć Logback dla Androida i aktywować logi, aby dzienniki mogły zostać przekierowane do pliku lub LogCat.

Jeśli naprawdę muszę usunąć dzienniki z ostatecznej biblioteki, dodaję do mojego pliku Proguard ( proguard-android-optimize.txtoczywiście po włączeniu pliku):

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

To nie działa z nowym kompilatorem Jacka
fattire

To mi pomogło; potrzebny był zarówno proguard-android-optimize.txtdomyślny plik Proguard, jak i -assumenosideeffectsniestandardowy plik Proguard! Używam shinkera R8 (obecnie domyślnego) i domyślnego logowania do Androida.
Jonik

10

Gorąco polecam użycie drewna z Jake Wharton

https://github.com/JakeWharton/timber

rozwiązuje problem z włączaniem / wyłączaniem oraz automatycznie dodaje klasę znaczników

właśnie

public class MyApp extends Application {

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

dzienniki będą używane tylko w wersji debugowania, a następnie użyj

Timber.d("lol");

lub

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

drukować

„Twoja klasa / msg” bez określania tagu


2
Timber jest bardzo fajny, ale jeśli masz już istniejący projekt - możesz spróbować github.com/zserge/log . Jest to drop-in zamiennik dla android.util.Log i ma większość funkcji, które ma Timber, a nawet więcej.
zserge,

zserge, twoje rozwiązanie do logowania wygląda dobrze. Wiele funkcji. Czy zastanawiałeś się nad dodaniem reguł Lint, takich jak Timber?
jk7

8

Użyłem klasy LogUtils jak w przykładowej aplikacji Google IO. Zmodyfikowałem to, aby użyć stałej DEBUG specyficznej dla aplikacji zamiast BuildConfig.DEBUG, ponieważ BuildConfig.DEBUG jest zawodny . Następnie w moich klasach mam następujące.

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

+1 za zgłoszenie błędu Build.DEBUG, którego użyłem. Zrezygnowałem także z różnych „poprawnych” obejść i zastosowałem rozwiązanie podobne do twojego stylu.
Richard Le Mesurier,

7

Rozważałbym skorzystanie z funkcji logowania roboguice zamiast wbudowanego android.util.Log

Ich funkcja automatycznie wyłącza dzienniki debugowania i pełne dla kompilacji wersji. Ponadto masz kilka fajnych funkcji za darmo (np. Konfigurowalne zachowanie rejestrowania, dodatkowe dane dla każdego dziennika i więcej)

Korzystanie z programu proguard może być dość kłopotliwe, a ja nie przejdę przez kłopot z konfiguracją i sprawieniem, aby działał z aplikacją, chyba że masz ku temu dobry powód (wyłączenie logów nie jest dobre)


Bardzo miłe podejście, gdy nie można użyć zaciemnienia .... szczególnie z powodu złamania roboguice z powodu proguard LOL
Snicolas

1
Zaktualizowany link do funkcji logowania robojuice
RenniePet

7

Publikuję to rozwiązanie, które dotyczy w szczególności użytkowników Androida Studio. Niedawno odkryłem Timber i pomyślnie go zaimportowałem do mojej aplikacji, wykonując następujące czynności:

Umieść najnowszą wersję biblioteki w swoim build.gradle:

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

Następnie w Android Studios, przejdź do Edycja -> Znajdź -> Zamień na ścieżce ...

Wpisz Log.e(TAG,lub jakkolwiek zdefiniowałeś swoje wiadomości w polu "Text to find"tekstowym. Następnie wystarczy go zastąpićTimber.e(

wprowadź opis zdjęcia tutaj

Kliknij Znajdź, a następnie zamień wszystko.

Android Studios przejrzy teraz wszystkie twoje pliki w projekcie i zastąpi wszystkie dzienniki drewna.

Jedynym problemem, jaki miałem z tą metodą, jest to, że gradle ma później milion komunikatów o błędach, ponieważ nie może znaleźć „Timber” w importach dla każdego z plików Java. Wystarczy kliknąć błędy, a Android Studios automatycznie zaimportuje „Timber” do Twojej java. Gdy zrobisz to dla wszystkich plików błędów, gradle ponownie się skompiluje.

Musisz także umieścić ten fragment kodu w onCreatemetodzie swojej Applicationklasy:

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

Spowoduje to rejestrowanie aplikacji tylko wtedy, gdy jesteś w trybie programowania, a nie w produkcji. Możesz również mieć BuildConfig.RELEASEdo logowania w trybie zwolnienia.


3
Spróbuj zrobić to samo w przypadku importu i upewnij się, że pole Wyrażenie regularne jest zaznaczone Tekst, aby znaleźć: import android\.util\.Log\;Zamień na:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson

lub możesz użyć wyszukiwania strukturalnego i zastąpić go jak pokazuje Chike Mgbemena w swoim poście
Maksim Turaev

@MaksimTuraev Twój link nie jest już istotny. Teraz jest blog o fryzurach.
Vadim Kotov

Wygląda na to, że post został usunięty = (nie można go nigdzie znaleźć.
Maksim Turaev

@MaksimTuraev tutaj jest kopia z maszyny Wayback, ale obrazy są zepsute - web.archive.org/web/20161004161318/http://chikemgbemena.com/…
Vadim Kotov

6

Na android.util.Log zapewnia sposób na włączenie / wyłączenie dziennika:

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

Domyślnie metoda isLoggable (...) zwraca false, tylko jeśli ustawisz w urządzeniu, jak to lubi:

adb shell setprop log.tag.MyAppTag DEBUG

Oznacza to, że można wydrukować dowolny dziennik powyżej poziomu DEBUG. Dokumentacja Androida:

Sprawdza, czy dziennik dla określonego znacznika może być rejestrowany na określonym poziomie. Domyślny poziom dowolnego znacznika jest ustawiony na INFO. Oznacza to, że każdy poziom powyżej, w tym INFO, zostanie zarejestrowany. Przed wykonaniem jakichkolwiek wywołań metody logowania należy sprawdzić, czy tag powinien zostać zalogowany. Możesz zmienić domyślny poziom, ustawiając właściwość systemową: 'setprop log.tag. „Gdzie poziom to VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT lub SUPPRESS. SUPPRESS wyłączy wszystkie rejestrowanie tagu. Możesz również utworzyć plik local.prop, który zawiera następujące elementy: 'log.tag. =' I umieścić go w /data/local.prop.

Możemy więc użyć niestandardowego logu:

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

Jeśli można uruchomić globalny zastąpić (raz), a potem zachować pewne standardy kodowania, można śledzić wzór często używane w Android ram .

Zamiast pisać

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

mieć to jako

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

Teraz proguard może usunąć StringBuilder oraz wszystkie ciągi i metody, których używa po drodze, ze zoptymalizowanego wydania DEX. Użyj proguard-android-optimize.txti nie musisz się martwić o android.util.Log w proguard-rules.pro:

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

Dzięki wtyczce stopniowej Android Studio jest dość niezawodny, więc nie potrzebujesz dodatkowych stałych do kontrolowania usuwania.BuildConfig.DEBUG


4

wprowadź opis zdjęcia tutaj

To właśnie robiłem w moich projektach na Androida.

W Android Studio możemy wykonać podobną operację, naciskając Ctrl + Shift + F, aby znaleźć z całego projektu (Command + Shift + F w MacOs) i Ctrl + Shift + R, aby zamienić ((Command + Shift + R w MacOs))


To wydaje się otwierać pracę z projektami zaćmienia. Opcja wyszukiwania nie jest nawet dostępna w studiach z Androidem.
Simon

2
w Android Studio możesz przeprowadzić podobne wyszukiwanie za pomocą skrótu Ctrl + Shift + F
Lins Louis

Przykładowy kod w pytaniu wyjaśnia, dlaczego nie jest to wiarygodne.
Nicolas Raoul,

Może to powodować problemy z usunięciem dowolnego polecenia zawartego w Dzienniku. Na przykład chocolateLog.recipie ();
Andrew S

Nie można znaleźć tej opcji dla Androida Studio 2.1. Mogę też użyć tej sztuczki na 1 pliku na raz, normalnie wyszukując / zamieniając.
VVB

3

Mam bardzo proste rozwiązanie. Używam IntelliJ do programowania, więc szczegóły są różne, ale pomysł powinien dotyczyć wszystkich IDE.

Wybieram katalog główny mojego drzewa źródłowego, klikam prawym przyciskiem myszy i wybieram „zamień”. Następnie wybieram zastąpienie wszystkich „Logów”. z „// Log.”. Spowoduje to usunięcie wszystkich instrukcji dziennika. Aby odłożyć je później, powtarzam tę samą zamianę, ale tym razem zastępuję wszystkie „// Log”. za pomocą „Log”.

Działa po prostu świetnie dla mnie. Pamiętaj tylko, aby ustawić zamień na wielkość liter, aby uniknąć wypadków, takich jak „Dialog”. Dla dodatkowej pewności możesz również zrobić pierwszy krok z „Log”. jako ciąg do wyszukania.

Znakomity.


2
Przeczytaj pytanie „Jeśli skomentuję wiersz dziennika” w moim pytaniu.
Nicolas Raoul,

OK, tak, powinienem częściej czytać po przejrzeniu odpowiedzi :). Jeśli masz takie przypadki, możesz chcieć innego rozwiązania, takiego jak sugerowane wcześniej, takiego jak umieszczenie wszystkich dzienników za innym interfejsem. Moja sugestia może być lepsza w przypadku mniejszych zespołów i projektów, w których ludzie chcą uniknąć dodatkowych kosztów rejestrowania bibliotek, dobrze znają ludzi i kod itp.
kg_sRy

1
Zastąpienie Log.d; // Log.d zajmuje się również tym scenariuszem „Jeśli”.
Jasper

3

Zgodnie z sugestią zserge ,

Timber jest bardzo fajny, ale jeśli masz już istniejący projekt - możesz spróbować github.com/zserge/log. Jest to drop-in zamiennik dla android.util.Log i ma większość funkcji, które ma Timber, a nawet więcej.

jego biblioteka dzienników zapewnia proste włączanie / wyłączanie drukowania dziennika jak poniżej.

Ponadto wymaga jedynie zmiany importwierszy i nic nie wymaga zmiany dla Log.d(...);instrukcji.

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT

Czy musisz umieścić ten wiersz kodu w każdym działaniu / fragmencie, czy tylko w jednym miejscu?
Noah Ternullo

@NoahTernullo // w pochodnym pliku aplikacji. DefaultApplication.java
Youngjae

3

Dodaj następujące do pliku proguard-rules.txt

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

1

Udoskonaliłem powyższe rozwiązanie, zapewniając obsługę różnych poziomów dziennika i automatycznie zmieniając poziomy dziennika w zależności od tego, czy kod jest uruchamiany na urządzeniu na żywo, czy na emulatorze.

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
Ten sam problem jak poprzednie rozwiązanie. Jeśli parametr ciągu jest budowany przy użyciu drogich wywołań, nadal marnuje zasoby. Sprawdzanie połączeń musi zostać wykonane przed budowaniem przekazanych parametrów.
type-a1pha

1

ProGuard zrobi to za Ciebie w kompilacji wydania, a teraz dobra wiadomość od android.com:

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

Narzędzie ProGuard zmniejsza, optymalizuje i zaciemnia kod, usuwając nieużywany kod i zmieniając nazwy klas, pól i metod za pomocą semantycznie niejasnych nazw. Wynikiem jest mniejszy plik .apk, który jest trudniejszy do odtworzenia. Ponieważ ProGuard utrudnia przekształcenie aplikacji w aplikację, ważne jest, aby używać jej, gdy aplikacja korzysta z funkcji wrażliwych na bezpieczeństwo, na przykład podczas licencjonowania aplikacji.

ProGuard jest zintegrowany z systemem kompilacji systemu Android, więc nie trzeba go wywoływać ręcznie. ProGuard działa tylko podczas budowania aplikacji w trybie wydania, więc nie musisz zajmować się zaciemnionym kodem podczas budowania aplikacji w trybie debugowania. Uruchomienie ProGuard jest całkowicie opcjonalne, ale wysoce zalecane.

W tym dokumencie opisano, jak włączyć i skonfigurować ProGuard, a także użyć narzędzia do śledzenia do dekodowania zaciemnionych śladów stosu


2
Wydaje się jednak, że domyślnie nie usuwa rejestrowania debugowania. Więc odpowiedź Christophera brzmi lepiej.
Nicolas Raoul,

0

Lubię używać Log.d (TAG, jakiś ciąg znaków, często String.format ()).

TAG jest zawsze nazwą klasy

Przekształć Log.d (TAG, -> Logd (w tekście twojej klasy

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

W ten sposób, gdy będziesz gotowy do wydania wersji, ustaw MainClass.debug na false!


1
Problem z tym i innymi rozwiązaniami oprócz tworzenia lub komentowania ich polega na tym, że zostawiasz się w kodzie, powodując prawdopodobnie dużą liczbę ciągów. w przeciętnej aplikacji nie stanowi to problemu, ale jeśli próbujesz zoptymalizować, staje się to problemem.
Lassi Kinnunen,

0

Dzienniki można usunąć za pomocą bash w systemie Linux i sed:

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

Działa dla dzienników wielowierszowych. W tym rozwiązaniu możesz mieć pewność, że dzienniki nie są obecne w kodzie produkcyjnym.


0

Wiem, że to stare pytanie, ale dlaczego nie zastąpiłeś wszystkich wywołań dziennika czymś w rodzaju logicznego logCallWasHere = true; // --- resztę twojego dziennika tutaj

Dlatego będziesz wiedział, kiedy chcesz je odłożyć, a nie wpłyną one na twoją instrukcję if :)


Interesujące, mam nadzieję, że takie linie są następnie ignorowane przez kompilator / optymalizator. Nazwa zmiennej musiałaby być jednak unikalna, ponieważ niektóre metody mają kilka wywołań dziennika i nie można dwukrotnie zadeklarować tej samej zmiennej.
Nicolas Raoul,

Możesz zadeklarować zmienną na górze aktywności i usunąć z tego wiersza deklarację boolowską;)
masood elsad

0

Dlaczego nie po prostu zrobić

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

? Żadne dodatkowe biblioteki nie są potrzebne, żadne reguły proguard, które mają tendencję do psucia projektu, a kompilator Java po prostu pominie kod bajtowy dla tego wywołania podczas tworzenia kompilacji wydania.


Niewygodne jest to, że jest bardziej gadatliwe niż pisanie Log.d("tag","msg");, a także łatwo zapomnieć o napisaniu if(BuildConfig.DEBUG)części.
Nicolas Raoul,

1
Innym problemem jest to, że ciągi pozostają w zapakowanym wydaniu.
straya,

0

Oto moje rozwiązanie, jeśli nie chcesz zadzierać z dodatkowymi bibliotekami lub edytować kodu ręcznie. Stworzyłem ten notatnik Jupyter, aby przeglądał wszystkie pliki Java i komentował wszystkie komunikaty dziennika. Nie idealnie, ale wykonało to za mnie zadanie.


0

Moja droga:

1) włącz tryb wyboru kolumny (alt + shift + wstaw)

2) wybierz jeden Log.d (TAG, „tekst”); część „Dziennik”.

3) następnie Shift + Ctrl + Alt + J

4) kliknij strzałkę w lewo

5) wykonaj Shift + End

6) naciśnij klawisz Delete.

to usuwa wszystkie wywołania LOG na raz w pliku Java.


0

Możesz spróbować użyć tej prostej konwencjonalnej metody:

Ctrl+ Shift+R

zastąpić

Log.e(

Z

// Log.e(

Nie działałoby to dobrze z przykładowym kodem podanym w pytaniu.
Nicolas Raoul

0

Łatwo z kotlinem, wystarczy zadeklarować kilka funkcji najwyższego poziomu

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

-1

najprostszy sposób;

posługiwać się DebugLog

Wszystkie dzienniki są wyłączane przez DebugLog po wydaniu aplikacji.

https://github.com/MustafaFerhan/DebugLog


To absolutnie źle. To tylko powoduje, że dzienniki nie są rejestrowane, nie usuwa ich z kodu, więc nadal tam są, aby pomóc ludziom w inżynierii wstecznej kodu, i nadal ma koszt formatowania ciągów wszystkich tych dzienników.
Glenn Maynard
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.