Wie entfernt man alle Debug-Logging-Aufrufe, bevor man die Release-Version einer Android-App erstellt?

Lesezeit: 11 Minuten

Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
Nikolaus Raul

Laut Google muss ich “Deaktivieren Sie alle Aufrufe von Log-Methoden im Quellcode” bevor ich meine Android-App bei Google Play veröffentliche. Auszug aus Abschnitt 3 der Checkliste für Veröffentlichungen:

Stellen Sie sicher, dass Sie die Protokollierung deaktivieren und die Debugging-Option deaktivieren, bevor Sie Ihre Anwendung für die Veröffentlichung erstellen. Sie können die Protokollierung deaktivieren, indem Sie Aufrufe von Log-Methoden in Ihren Quelldateien entfernen.

Mein Open-Source-Projekt ist groß und es ist mühsam, es jedes Mal manuell zu tun, wenn ich es veröffentliche. Darüber hinaus ist das Entfernen einer Protokollzeile möglicherweise schwierig, zum Beispiel:

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

Wenn ich die Log-Zeile kommentiere, gilt die Bedingung für die nächste Zeile, und es besteht die Möglichkeit, dass load() nicht aufgerufen wird. Sind solche Situationen selten genug, dass ich entscheiden kann, dass es sie nicht geben sollte?

Gibt es also einen besseren Weg, dies auf Quellcodeebene zu tun? Oder vielleicht eine clevere ProGuard-Syntax, um alle Protokollzeilen effizient, aber sicher zu entfernen?

  • +1, weil ich mich nicht daran erinnerte, dass dies in der Veröffentlichungs-Checkliste stand.

    – rts

    22. Juli 2011 um 16:08 Uhr

  • Um eine nicht blockierte Zeile auszukommentieren, verwende ich “;//” anstelle von “//”.

    – yingted

    5. Januar 2012 um 21:02 Uhr


  • Wenn Sie dies rückgängig machen müssen, möchten Sie wahrscheinlich verwenden sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g' stattdessen.

    – yingted

    5. Januar 2012 um 21:08 Uhr


  • Der Link, den Dimitar hinzugefügt hat, funktioniert nicht mehr. Ich habe stattdessen das gefunden source.android.com/source/code-style.html#log-sparingly.

    – Josef L

    30. März 2013 um 3:00 Uhr

  • @mboy: Heutzutage wahrscheinlich hauptsächlich wegen der Leistung, aber bei alten Android-Versionen hat es auch Sicherheitsvorteile.

    – Nikolaus Raoul

    12. November 2015 um 2:46 Uhr

Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
Christoph Orr

Ich finde eine viel einfachere Lösung ist, all das zu vergessen if Kontrolliert überall und einfach verwenden ProGuard irgendwelche abzustreifen Log.d() oder Log.v() Methodenaufrufe, wenn wir unsere Ant aufrufen release Ziel.

Auf diese Weise haben wir immer die Debug-Informationen, die für reguläre Builds ausgegeben werden, und müssen keine Codeänderungen für Release-Builds vornehmen. ProGuard kann den Bytecode auch mehrfach durchlaufen, um andere unerwünschte Anweisungen und leere Blöcke zu entfernen, und kann gegebenenfalls kurze Methoden automatisch einbetten.

Hier ist zum Beispiel eine sehr einfache ProGuard-Konfiguration für Android:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

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

Sie würden das also in einer Datei speichern, dann ProGuard von Ant aus aufrufen und Ihr gerade kompiliertes JAR und das JAR der Android-Plattform, das Sie verwenden, übergeben.

Siehe auch die Beispiele im ProGuard-Handbuch.


Update (4,5 Jahre später): Heutzutage habe ich verwendet Holz für Android-Protokollierung.

Es ist nicht nur ein bisschen schöner als die Standardeinstellung Log -Implementierung – das Protokoll-Tag wird automatisch gesetzt, und es ist einfach, formatierte Zeichenfolgen und Ausnahmen zu protokollieren – aber Sie können auch zur Laufzeit unterschiedliche Protokollierungsverhalten festlegen.

In diesem Beispiel werden Protokollierungsanweisungen nur in Debug-Builds meiner App in logcat geschrieben:

Holz ist in meinem eingerichtet Application onCreate() Methode:

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

Dann kann ich mich an einer anderen Stelle in meinem Code einfach anmelden:

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

Siehe die Holzmuster-App für ein fortgeschritteneres Beispiel, bei dem alle Protokollanweisungen während der Entwicklung an logcat gesendet werden und in der Produktion keine Debug-Anweisungen protokolliert werden, aber Fehler stillschweigend an Crashlytics gemeldet werden.

  • Und warum ist das nicht in der Standard-Proguard-Datei?

    – rts

    22. Juli 2011 um 16:08 Uhr

  • +rds, da die Zeilennummern der Produktions-Stacktraces sich von denen in Ihrem Code unterscheiden, da Zeilen entfernt werden.

    – Kerl

    21. Februar 2012 um 11:01 Uhr

  • Ich kann bestätigen, dass durch das Entfernen von Protokollanrufen Zeilennummern in Stacktraces verschoben werden. Es wird nicht immer asynchron sein (ich habe mehrere Schnelltests durchgeführt, kann aber nicht genau feststellen, was die Ursache ist, möglicherweise wenn Sie eine Zeichenfolge im Protokollaufruf verketten), aber manchmal werden ein paar Zeilen daneben liegen. Die Mühe lohnt sich IMO für die Möglichkeit, Protokollanrufe einfach zu entfernen.

    – Toni Chan

    15. Juni 2012 um 18:37 Uhr

  • @Fraggle Von proguard-android.txt in ADT-Tools: „Beachten Sie, dass Sie, wenn Sie die Optimierung aktivieren möchten, nicht einfach Optimierungs-Flags in Ihre eigene Projektkonfigurationsdatei aufnehmen können; stattdessen müssen Sie auf die Datei „proguard-android-optimize. txt”-Datei statt dieser aus Ihrer” # project.properties-Datei.

    – Raanan

    7. Dezember 2012 um 11:04 Uhr


  • Wie espinchi in der folgenden Antwort sagte. “Das einzige Problem bei diesem Ansatz ist, dass, wenn Sie Log.d(“tag”, “Processed: ” + new ItemCounter(blabla) + ” items “) ausführen, auch wenn diese Protokollnachricht nicht in Ihrer veröffentlichten Version erscheint, Zum Erstellen der Nachricht wird ein StringBuilder verwendet, dessen Erstellung teuer sein könnte.“ Gilt das auch für Timber?

    – Chitrang

    17. Mai 2016 um 6:21 Uhr


1646541197 261 Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
Reiner

Alles gute Antworten, aber als ich mit meiner Entwicklung fertig war, wollte ich weder if-Anweisungen um alle Log-Aufrufe herum verwenden, noch wollte ich externe Tools verwenden.

Die Lösung, die ich verwende, besteht also darin, die android.util.Log-Klasse durch meine eigene Log-Klasse zu ersetzen:

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

Das einzige, was ich in allen Quelldateien tun musste, war, den Import von android.util.Log durch meine eigene Klasse zu ersetzen.

  • Das einzige Problem bei diesem Ansatz ist, dass, wenn Sie Log.d(“tag”, “Processed: ” + new ItemCounter(blabla) + ” items “) ausführen, auch wenn diese Protokollnachricht nicht in Ihrer veröffentlichten Version erscheint, a StringBuilder wird verwendet, um die Nachricht zu erstellen, deren Erstellung teuer sein könnte.

    – Espinchi

    7. September 2011 um 17:06 Uhr

  • Diese Lösung hat ein großes Problem. Espinchi erwähnte nur die Spitze des Eisbergs. Das Problem ist, dass, wenn Sie anrufen Log.d("tag", someValue.toString()); dass es sehr leicht ist, zu vergessen, dass someValue nicht null ist, was bedeutet, dass es möglicherweise a auslöst NullPointerException in Produktion. Es schlägt eine sichere Lösung vor, aber es wird Sie täuschen. Wir uns a private static boolean DEBUG und dann if(DEBUG)Log.d(TAG, msg);

    – Philipp

    8. August 2012 um 19:10 Uhr

  • @espinchi Ihre Bedenken scheinen für alle Protokollierungsbibliotheken zu gelten, wie in dieser Antwort beschrieben stackoverflow.com/a/15452492/433718 (Slf4j, Rückstand, …). Wird es nicht empfohlen, sie zu verwenden?

    – Eine Welt

    20. Juni 2013 um 20:14 Uhr

  • Die einzige Möglichkeit, den im ersten Kommentar von @espinchi erwähnten Overhead zu minimieren, besteht darin, die Protokollierungsmethoden so zu ändern, dass sie stattdessen Varargs akzeptieren String. Vollständige Lösung wird beschrieben Hier. Dies hat offenbar einen weiteren Nachteil: Jeder Aufruf sollte bearbeitet werden (nicht nur eine Importzeile).

    – Stan

    27. November 2013 um 15:17 Uhr

  • Nur zu Ihrer Information, wenn Sie Android Studio und das Gradle-Build-System verwenden, können Sie es verwenden static final boolean LOG = BuildConfig.DEBUG und müssen diese Datei niemals ändern.

    – Aschischduh

    20. Mai 2014 um 19:23 Uhr


Ich schlage vor, irgendwo einen statischen booleschen Wert zu haben, der angibt, ob geloggt werden soll oder nicht:

class MyDebug {
  static final boolean LOG = true;
}

Wo immer Sie sich dann mit Ihrem Code anmelden möchten, tun Sie einfach Folgendes:

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

Wenn Sie jetzt MyDebug.LOG auf false setzen, entfernt der Compiler den gesamten Code in solchen Prüfungen (da es sich um ein statisches Finale handelt, weiß er zur Kompilierzeit, dass kein Code verwendet wird).

Bei größeren Projekten möchten Sie möglicherweise Boolesche Werte in einzelne Dateien einfügen, um die Protokollierung dort nach Bedarf einfach aktivieren oder deaktivieren zu können. Dies sind beispielsweise die verschiedenen Protokollierungskonstanten, die wir im Fenstermanager haben:

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;

Mit entsprechendem Code wie:

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

  • Ich würde auch für einen solchen Ansatz stimmen. Es wird auch im offiziellen In-App-Abrechnungsbeispiel von Google verwendet.

    – LA_

    5. April 2011 um 16:56 Uhr

  • Wäre es nicht weniger ausführlich, die Bedingung als ersten Parameter zu übergeben?

    – Snikola

    31. August 2012 um 12:36 Uhr

  • Dies scheint die beste Lösung zu sein, obwohl es zusätzlichen Code für jede Protokollanweisung erfordert: Zeilennummern werden beibehalten (Schwäche des ProGuard-Ansatzes), es wird kein Code zum Erstellen einer Protokollnachricht ausgeführt (Schwäche des Wrapper-Klassen-Ansatzes und anscheinend auch des Ansatzes der Protokollierungsbibliothek). . Die Verwendung dieses Ansatzes in Googles In-App-Abrechnungsbeispiel gemäß @LA_ unterstützt meine Gedanken ebenfalls.

    – Eine Welt

    20. Juni 2013 um 20:47 Uhr


  • @Snicolas Wie können Sie die Bedingung als ersten Parameter übergeben, ohne einen Wrapper zu implementieren? Wenn Sie es außerdem als Parameter hinzufügen, müssen vor dem Eingeben der Methode alle Parameter ausgewertet werden, dh auch der Nachrichtenstring. Die Bedingung muss getestet werden, bevor die Parameter erstellt werden. Die vorgeschlagene Lösung ist möglicherweise die beste, wenn kein externes Tool vorhanden ist.

    – Typ-a1pha

    21. Juli 2013 um 12:45 Uhr

  • In Bezug auf den Binärcode ist dies am besten. Aber eine solche Codierung ist nur ein großer Aufwand für eine einfache Debug-Log-Ausgabe. Die Lesbarkeit des Codes sinkt erheblich. Einige gewinnen, einige verlieren, schätze ich…

    – Richard Le Mesurier

    12. Dezember 2013 um 8:19 Uhr

Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
Nikolaus Raul

Christophers Proguard-Lösung ist die beste, aber wenn Sie Proguard aus irgendeinem Grund nicht mögen, hier ist eine sehr technisch einfache Lösung:

Kommentarprotokolle:

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

Protokolle auskommentieren:

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

Eine Einschränkung besteht darin, dass sich Ihre Protokollierungsanweisungen nicht über mehrere Zeilen erstrecken dürfen.

(Führen Sie diese Zeilen in einer UNIX-Shell im Stammverzeichnis Ihres Projekts aus. Wenn Sie Windows verwenden, erhalten Sie eine UNIX-Schicht oder verwenden Sie entsprechende Windows-Befehle.)

1646541198 211 Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
Vincent Hiribarren

Ich möchte einige Präzisierungen zur Verwendung von Proguard mit Android Studio und Gradle hinzufügen, da ich viele Probleme hatte, Protokollzeilen aus der endgültigen Binärdatei zu entfernen.

Damit assumenosideeffects in Proguard funktioniert, gibt es eine Voraussetzung.

In Ihrer Gradle-Datei müssen Sie die Verwendung der proguard-android-optimize.txt als Standarddatei.

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

Eigentlich in der Voreinstellung proguard-android.txt Datei wird die Optimierung mit den beiden Flags deaktiviert:

-dontoptimize
-dontpreverify

Die proguard-android-optimize.txt Datei fügt diese Zeilen nicht hinzu, also jetzt assumenosideeffects kann arbeiten.

Dann verwende ich persönlich SLF4J, umso mehr, wenn ich einige Bibliotheken entwickle, die an andere verteilt werden. Der Vorteil ist, dass standardmäßig keine Ausgabe erfolgt. Und wenn der Integrator Protokollausgaben wünscht, kann er Logback für Android verwenden und die Protokolle aktivieren, sodass Protokolle in eine Datei oder an LogCat umgeleitet werden können.

Wenn ich die Protokolle wirklich aus der endgültigen Bibliothek entfernen muss, füge ich sie meiner Proguard-Datei hinzu (nachdem ich die proguard-android-optimize.txt Datei natürlich):

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

  • Dies funktioniert nicht mit dem neuen Jack-Compiler – stackoverflow.com/questions/37932114/…

    – Fattire

    8. März 2017 um 5:02 Uhr

  • Das hat mir geholfen; beide proguard-android-optimize.txt als Standard-Proguard-Datei und -assumenosideeffects in benutzerdefinierter Proguard-Datei benötigt! Ich verwende R8 Shinker (heutzutage die Standardeinstellung) und die standardmäßige Android-Protokollierung.

    – Jonik

    28. April 2020 um 15:45 Uhr

1646541199 248 Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
AndroidGecko

Ich empfehle dringend, Timber von Jake Wharton zu verwenden

https://github.com/JakeWharton/timber

Es löst Ihr Problem mit dem Aktivieren / Deaktivieren und fügt die Tag-Klasse automatisch hinzu

gerade

public class MyApp extends Application {

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

Protokolle werden nur in Ihrer Debug-Version verwendet und dann verwendet

Timber.d("lol");

oder

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

zu drucken

“Ihre Klasse / msg” ohne Angabe des Tags

  • Dies funktioniert nicht mit dem neuen Jack-Compiler – stackoverflow.com/questions/37932114/…

    – Fattire

    8. März 2017 um 5:02 Uhr

  • Das hat mir geholfen; beide proguard-android-optimize.txt als Standard-Proguard-Datei und -assumenosideeffects in benutzerdefinierter Proguard-Datei benötigt! Ich verwende R8 Shinker (heutzutage die Standardeinstellung) und die standardmäßige Android-Protokollierung.

    – Jonik

    28. April 2020 um 15:45 Uhr

1646541199 431 Wie entfernt man alle Debug Logging Aufrufe bevor man die Release Version einer
vmann

Ich habe eine verwendet LogUtils Klasse wie in der Google IO-Beispielanwendung. Ich habe dies geändert, um eine anwendungsspezifische DEBUG-Konstante anstelle von BuildConfig.DEBUG zu verwenden, weil BuildConfig.DEBUG ist unzuverlässig. Dann habe ich in meinen Klassen Folgendes.

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 für Fehlerbericht an Build.DEBUG die ich früher benutzt habe. Ich habe auch die verschiedenen “richtigen” Problemumgehungen aufgegeben und verwende eine ähnliche Stillösung wie Sie.

    – Richard Le Mesurier

    12. Dezember 2013 um 8:42 Uhr

952830cookie-checkWie entfernt man alle Debug-Logging-Aufrufe, bevor man die Release-Version einer Android-App erstellt?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy