449 Stimmen

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

Laut Google muss ich " alle Aufrufe von Log-Methoden im Quellcode deaktivieren ", 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 ausschalten, 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 bearbeiten, wenn ich eine neue Version herausbringe. Außerdem ist das Entfernen einer Log-Zeile potenziell schwierig, zum Beispiel:

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

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

Gibt es also eine bessere Methode auf Quellcode-Ebene, um das zu tun? Oder vielleicht eine clevere ProGuard-Syntax, um alle Log-Zeilen effizient aber sicher zu entfernen?

2 Stimmen

+1 weil ich mich nicht daran erinnert habe, dass dies in der Checkliste für die Veröffentlichung enthalten ist.

55 Stimmen

Um eine nicht blockierte Zeile auszukommentieren, verwende ich ";//" anstelle von "//".

0 Stimmen

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

536voto

Christopher Orr Punkte 108221

Ich finde, eine viel einfachere Lösung ist es, alles zu vergessen if Schecks überall und verwenden Sie einfach ProGuard zum Entfernen aller Log.d() o Log.v() Methode aufruft, wenn wir unsere Ant 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 auch mehrere Durchläufe über den Bytecode durchführen, um unerwünschte Anweisungen und leere Blöcke zu entfernen, und kann gegebenenfalls automatisch kurze Methoden einfügen.

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 diese also in einer Datei speichern und ProGuard dann von Ant aus aufrufen, wobei Sie Ihr soeben kompiliertes JAR und das von Ihnen verwendete JAR für die Android-Plattform übergeben.

Siehe auch die Beispiele im ProGuard-Handbuch.


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

Sie ist nicht nur ein bisschen schöner als die Standard Log Implementierung - das Log-Tag wird automatisch gesetzt, und es ist einfach, formatierte Zeichenketten und Ausnahmen zu protokollieren -, aber Sie können auch verschiedene Logging-Verhaltensweisen zur Laufzeit festlegen.

In diesem Beispiel werden die Logging-Anweisungen nur in Debug-Builds meiner Anwendung in logcat geschrieben:

Timber ist in meinem Application onCreate() Methode:

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

Dann kann ich überall sonst in meinem Code leicht protokollieren:

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

Siehe die Beispielanwendung Holz für ein fortgeschrittenes Beispiel, bei dem alle Log-Statements während der Entwicklung an logcat gesendet werden und in der Produktion keine Debug-Statements protokolliert werden, sondern Fehler stillschweigend an Crashlytics gemeldet werden.

0 Stimmen

Für Android richtig? Ist Ihr Projekt Open-Source? Könnte ich Ihre Ant-Datei irgendwo bekommen? :-) Herzlichen Dank!

0 Stimmen

Ich habe eine einfache Konfigurationsdatei hinzugefügt, die Sie verwenden können, wenn Sie ProGuard von einem Ant-Ziel aufrufen.

74 Stimmen

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

135voto

Reiner Punkte 1353

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

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.

151 Stimmen

Das einzige Problem bei diesem Ansatz ist, dass Sie in diesem Fall Log.d("tag", "Processed: " + new ItemCounter(blabla) + " items "), selbst wenn diese Log-Meldung in Ihrer freigegebenen Version nicht erscheint, ein StringBuilder verwendet wird, um die Meldung zu erstellen, was teuer sein könnte.

10 Stimmen

Diese Lösung hat ein großes Problem. espinchi hat nur die Spitze des Eisbergs erwähnt. Das Problem ist, dass beim Aufruf von Log.d("tag", someValue.toString()); dass man sehr leicht vergessen kann, someValue daraufhin zu überprüfen, dass es nicht null ist, was dazu führt, dass es zu einem NullPointerException in der Produktion. Sie suggeriert eine sichere Lösung, aber sie wird Sie täuschen. Wir haben eine private static boolean DEBUG y luego if(DEBUG)Log.d(TAG, msg);

0 Stimmen

Würde ProGuard (mit defaut-Einstellungen) diese leeren Log.d()-Aufrufe nicht als unbenutzten Code erkennen und sie entfernen?

63voto

hackbod Punkte 89543

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

class MyDebug {
  static final boolean LOG = true;
}

Wo auch immer Sie Ihren Code eintragen wollen, tun Sie dies:

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

Wenn Sie nun MyDebug.LOG auf false setzen, entfernt der Compiler den gesamten Code innerhalb solcher Prüfungen (da es sich um ein statisches Final handelt, weiß er zur Kompilierzeit, dass der Code nicht verwendet wird).

Bei größeren Projekten kann es sinnvoll sein, in einzelnen Dateien Boolesche Werte zu verwenden, um die Protokollierung je nach Bedarf zu aktivieren oder zu deaktivieren. Dies sind zum Beispiel 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 + ")");

1 Stimmen

Ich würde auch für einen solchen Ansatz stimmen. Er wird auch im offiziellen Google-Beispiel für die In-App-Rechnung verwendet.

4 Stimmen

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

1 Stimmen

Dies scheint die beste Lösung zu sein, obwohl sie zusätzlichen Code für jede Protokollanweisung erfordert: Zeilennummern werden beibehalten (Schwäche des ProGuard-Ansatzes), es wird kein Code zur Erstellung der Protokollmeldung ausgeführt ( Schwächen des Wrapper-Klassen-Ansatzes und offenbar auch der Ansatz der Protokollierungsbibliothek). Die Verwendung dieses Ansatzes in Googles In-App-Billing-Beispiel nach @LA_ unterstützt meine Gedanken ebenfalls.

32voto

Nicolas Raoul Punkte 57157

Die Proguard-Lösung von Christopher ist die beste, aber wenn Sie aus irgendeinem Grund Proguard nicht mögen, gibt es eine sehr einfache Lösung:

Kommentarprotokolle:

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

Unkommentierte Protokolle:

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

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

(Führen Sie diese Zeilen in einer UNIX-Shell an der Wurzel Ihres Projekts aus. Wenn Sie Windows verwenden, holen Sie sich eine UNIX-Ebene oder verwenden Sie entsprechende Windows-Befehle)

1 Stimmen

Braucht ein "" nach dem -i in Sed, wenn es auf einem Mac läuft (wie bei diese ) Danke.

0 Stimmen

Ich habe das Gefühl, dass ich das für etwas, an dem ich arbeite, verwenden werde, weil ich mit Proguard nicht viel Glück hatte.

0 Stimmen

Und was, wenn Sie ein Log nach einem nicht eingeklammerten while-Zweig haben, wie Sie in Ihrem ersten Beitrag vorgeschlagen haben?

20voto

Vincent Hiribarren Punkte 5084

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

Für die Herstellung von 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'
    }
}

In der Standardeinstellung proguard-android.txt Datei wird die Optimierung mit den beiden Flags deaktiviert:

-dontoptimize
-dontpreverify

En proguard-android-optimize.txt Datei fügt diese Zeilen nicht hinzu, so dass jetzt assumenosideeffects funktionieren kann.

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

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

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

0 Stimmen

Das funktioniert nicht mit dem neuen Jack-Compiler. stackoverflow.com/questions/37932114/

0 Stimmen

Das hat mir geholfen; sowohl proguard-android-optimize.txt als Standard-Proguard-Datei und -assumenosideeffects in der benutzerdefinierten Proguard-Datei erforderlich waren! Ich benutze R8 shinker (der Standard heutzutage) und Standard-Android-Protokollierung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X