Wie entferne ich alle Debug-Protokollierungsaufrufe, bevor ich die Release-Version einer Android-App erstelle?

397

Laut Google muss ich " alle Aufrufe von Protokollierungsmethoden im Quellcode deaktivieren ", bevor ich meine Android-App bei Google Play veröffentliche. Auszug aus Abschnitt 3 der Publikations-Checkliste :

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

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

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

Wenn ich die Protokollzeile 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 sie nicht existieren sollten?

Gibt es eine bessere Möglichkeit, dies auf Quellcode-Ebene zu tun? Oder vielleicht eine clevere ProGuard-Syntax, um alle Protokollzeilen effizient, aber sicher zu entfernen?

Nicolas Raoul
quelle
2
+1, weil ich mich nicht daran erinnerte, dass dies in der Publikations-Checkliste stand.
rds
51
Um eine nicht blockierte Zeile auskommentieren zu können, verwende ich "; //" anstelle von "//".
Yingted
Wenn Sie dies rückgängig machen müssen, möchten Sie wahrscheinlich sed 's_^\(\s*Log\.\)_;//'`date|tr -s \ -`'\1_g'stattdessen verwenden.
Yingted
2
Der von Dimitar hinzugefügte Link funktioniert nicht mehr. Ich fand dies stattdessen source.android.com/source/code-style.html#log-sparingly .
JosephL
1
@mboy: Wahrscheinlich hauptsächlich für die Leistung heutzutage, aber auf alten Android-Versionen hat es auch Sicherheitsvorteile.
Nicolas Raoul

Antworten:

488

Ich finde eine viel einfachere Lösung, alle ifÜberprüfungen überall zu vergessen und einfach ProGuard zu verwenden, um alle Log.d()oder Log.v()Methodenaufrufe zu entfernen, wenn wir unser Ant- releaseZiel aufrufen .

Auf diese Weise werden immer die Debug-Informationen für reguläre Builds ausgegeben und wir 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 gegebenenfalls automatisch kurze Methoden einbinden.

Hier ist zum Beispiel eine sehr grundlegende 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 und dann ProGuard von Ant aus aufrufen und Ihre gerade kompilierte JAR und die von Ihnen verwendete Android-Plattform-JAR übergeben.

Siehe auch die Beispiele im ProGuard-Handbuch.


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

Es ist nicht nur ein bisschen schöner als die Standardimplementierung Log- das Protokoll-Tag wird automatisch festgelegt und es ist einfach, formatierte Zeichenfolgen und Ausnahmen zu protokollieren -, sondern Sie können auch zur Laufzeit unterschiedliche Protokollierungsverhalten angeben.

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

Holz wird in meiner Application onCreate()Methode eingerichtet:

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

Dann kann ich mich überall in meinem Code einfach anmelden:

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

In der Timber-Beispiel-App finden Sie ein erweitertes Beispiel, in dem alle Protokollanweisungen während der Entwicklung an logcat gesendet werden und in der Produktion keine Debug-Anweisungen protokolliert werden, Fehler jedoch unbemerkt an Crashlytics gemeldet werden.

Christopher Orr
quelle
59
Und warum ist das nicht in der Standard-Proguard-Datei?
rds
10
+ rds, da dadurch die Zeilennummern der Produktionsstapel von denen in Ihrem Code abweichen, wenn Zeilen entfernt werden.
Guy
5
Ich kann bestätigen, dass durch das Entfernen von Protokollaufrufen die Zeilennummern in Stapelspuren verschoben werden. Es ist nicht immer nicht synchron (ich habe mehrere schnelle Tests durchgeführt, kann aber die Ursache nicht genau bestimmen, möglicherweise wenn Sie eine Zeichenfolge im Protokollaufruf verketten), aber manchmal sind es ein paar Zeilen entfernt. Lohnt sich die Mühe IMO für die Möglichkeit, Protokollaufrufe einfach zu entfernen.
Tony Chan
5
@Fraggle Von proguard-android.txt in ADT-Tools: "Beachten Sie, dass Sie, wenn Sie die Optimierung aktivieren möchten, nicht nur Optimierungsflags in Ihre eigene Projektkonfigurationsdatei aufnehmen können, sondern stattdessen auf" proguard-android-optimize "verweisen müssen. txt "Datei anstelle dieser aus Ihrer" # project.properties Datei.
Raanan
3
Wie espinchi unten sagte, antworte. "Das einzige Problem bei diesem Ansatz ist, dass, wenn Sie Log.d (" tag "," Verarbeitet: "+ neuer ItemCounter (blabla) +" items ") ausführen, auch wenn diese Protokollmeldung in Ihrer freigegebenen Version nicht angezeigt wird, Zum Erstellen der Nachricht wird ein StringBuilder verwendet, dessen Erstellung teuer sein kann. "Trifft dies auch im Timber-Fall zu?
Chitrang
117

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 verwenden.

Die Lösung, die ich verwende, besteht 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.

Reiner
quelle
143
Das einzige Problem bei diesem Ansatz besteht darin, dass, wenn Sie Log.d ("tag", "Verarbeitet:" + neuer ItemCounter (blabla) + "items") ausführen, auch wenn diese Protokollmeldung in Ihrer freigegebenen Version nicht angezeigt wird, a StringBuilder wird zum Erstellen der Nachricht verwendet, deren Erstellung teuer sein kann.
Espinchi
9
Diese Lösung hat ein großes Problem. Espinchi erwähnte nur die Spitze des Eisbergs. Das Problem ist, dass es beim Aufrufen Log.d("tag", someValue.toString());sehr leicht zu vergessen ist, someValue auf Null zu überprüfen, was bedeutet, dass möglicherweise ein Wert NullPointerExceptionin der Produktion ausgelöst wird . Es schlägt eine sichere Lösung vor, aber es wird Sie austricksen. Wir uns ein private static boolean DEBUGund dannif(DEBUG)Log.d(TAG, msg);
Philipp
2
@espinchi Ihr Anliegen scheint auf alle Protokollierungsbibliotheken zuzutreffen, wie in dieser Antwort beschrieben. stackoverflow.com/a/15452492/433718 (Slf4j, backlog, ...). Wird nicht empfohlen, sie zu verwenden?
OneWorld
1
Die einzige Möglichkeit, den im ersten Kommentar von @espinchi erwähnten Overhead zu minimieren, besteht darin, die Protokollierungsmethoden so zu ändern, dass stattdessen varargs akzeptiert werden String. Komplettlösung ist decribed hier . Dies hat anscheinend einen weiteren Nachteil: Jeder Anruf sollte bearbeitet werden (nicht nur eine Importzeile).
Stan
21
Nur zu Ihrer Information: Wenn Sie Android Studio und Gradle Build System verwenden, können Sie static final boolean LOG = BuildConfig.DEBUGdiese Datei verwenden und müssen sie nie ändern.
Ashishduh
61

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

Klasse MyDebug {
  statisches endgültiges boolesches LOG = true;
}}

Dann tun Sie einfach, wo immer Sie Ihren Code anmelden möchten:

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

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

Bei größeren Projekten möchten Sie möglicherweise Boolesche Werte in einzelnen Dateien verwenden, um die Protokollierung dort bei 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 + ")");
Hackbod
quelle
1
Ich würde auch für einen solchen Ansatz stimmen. Es wird auch im offiziellen In-App-Abrechnungsbeispiel von Google verwendet.
LA_
4
Wäre es nicht weniger ausführlich, die Bedingung als ersten Parameter zu übergeben?
Snicolas
1
Dies scheint die beste Lösung zu sein, obwohl für jede Protokollanweisung zusätzlicher Code erforderlich ist: Zeilennummern bleiben erhalten (Schwachstelle des ProGuard-Ansatzes), Es wird kein Code zum Erstellen einer Protokollnachricht ausgeführt ( Schwachstelle des Wrapper-Klassenansatzes und anscheinend auch des Protokollierungsbibliotheksansatzes). . Die Verwendung dieses Ansatzes in Googles im App-Abrechnungsbeispiel gemäß @LA_ unterstützt auch meine Gedanken.
OneWorld
2
@Snicolas Wie können Sie die Bedingung als ersten Parameter übergeben, ohne einen Wrapper zu implementieren? Wenn Sie es als Parameter hinzufügen, müssen vor der Eingabe der Methode alle Parameter ausgewertet werden, dh auch die Nachrichtenzeichenfolge. Die Bedingung muss vor dem Erstellen der Parameter getestet werden. Die vorgeschlagene Lösung ist möglicherweise die beste, wenn kein externes Tool vorhanden ist.
Typ-a1pha
2
Binärcode weise ist dies am besten. Das Codieren wie dieses ist jedoch nur ein großer Aufwand für eine einfache Debug-Protokollausgabe. Die Lesbarkeit des Codes nimmt erheblich ab. Gewinnen Sie einige, verlieren Sie einige, denke ich ...
Richard Le Mesurier
30

Die Proguard-Lösung von Christopher ist die beste, aber wenn Sie Proguard aus irgendeinem Grund nicht mögen, finden Sie hier eine sehr Low-Tech-Lösung:

Kommentarprotokolle:

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

Kommentarprotokolle:

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, rufen Sie eine UNIX-Ebene ab oder verwenden Sie entsprechende Windows-Befehle.)

Nicolas Raoul
quelle
1
braucht einen „“ nach dem -i in Sed wenn läuft auf Mac (wie pro diesen ) Vielen Dank.
Vishal
Ich habe das Gefühl, dass dies das ist, was ich letztendlich für etwas benutze, an dem ich arbeite, weil ich mit Proguard überhaupt nicht viel Glück hatte
Joe Plante
Und was ist, wenn Sie ein Protokoll nach einem nicht in Klammern gesetzten Zweig haben, wie Sie in Ihrem ersten Beitrag vorgeschlagen haben?
Typ-a1pha
@ type-a1pha: Wenn Sie diese Lösung übernehmen, müssen Sie Klammerblöcke als obligatorisch betrachten.
Nicolas Raoul
2
@NicolasRaoul Das Semikolon behebt dieses Problem ( //vs. ;//)
Alex Gittemeier
18

Ich möchte einige Präzisionen 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.

Um assumenosideeffectsin Proguard Werke zu machen , gibt es eine Voraussetzung.

In Ihrer Gradle-Datei müssen Sie die Verwendung der proguard-android-optimize.txtStandarddatei angeben .

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 Standarddatei proguard-android.txtist die Optimierung mit den beiden Flags deaktiviert:

-dontoptimize
-dontpreverify

Die proguard-android-optimize.txtDatei fügt diese Zeilen nicht hinzu, sodass sie jetzt assumenosideeffectsfunktionieren können.

Dann persönlich verwende ich SLF4J , umso mehr, wenn ich einige Bibliotheken entwickle, die an andere verteilt werden. Der Vorteil ist, dass standardmäßig keine Ausgabe erfolgt. Wenn der Integrator einige Protokollausgaben wünscht, kann er Logback für Android verwenden und die Protokolle aktivieren, sodass Protokolle in eine Datei oder in 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.txtDatei natürlich aktiviert habe ):

-assumenosideeffects class * implements org.slf4j.Logger {
    public *** trace(...);
    public *** debug(...);
    public *** info(...);
    public *** warn(...);
    public *** error(...);
}
Vincent Hiribarren
quelle
Das funktioniert nicht mit dem neuen Jack compiler-- stackoverflow.com/questions/37932114/...
fattire
Das hat mir geholfen; Es wurden sowohl proguard-android-optimize.txtals Standard-Proguard-Datei als auch -assumenosideeffectsals benutzerdefinierte Proguard-Datei benötigt! Ich verwende R8 Shinker (heutzutage die Standardeinstellung) und die Standard-Android-Protokollierung.
Jonik
10

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 automatisch Tag-Klassen 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

"Deine Klasse / Nachricht" ohne Angabe des Tags

AndroidGecko
quelle
2
Timber ist sehr schön, aber wenn Sie bereits ein Projekt haben, können Sie github.com/zserge/log ausprobieren . Es ist ein Ersatz für android.util.Log und bietet die meisten Funktionen von Timber und noch mehr.
Zserge
zserge, Ihre Protokolllösung sieht gut aus. Viele Funktionen. Haben Sie darüber nachgedacht, Flusenregeln wie bei Timber hinzuzufügen?
jk7
8

Ich habe eine LogUtils- Klasse wie in der Google IO-Beispielanwendung verwendet. Ich habe dies geändert, um eine anwendungsspezifische DEBUG-Konstante anstelle von BuildConfig.DEBUG zu verwenden, da BuildConfig.DEBUG unzuverlässig ist . 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");
  }
}
JosephL
quelle
+1 für Fehlerbericht Build.DEBUG, den ich früher verwendet habe. Ich habe auch die verschiedenen "richtigen" Problemumgehungen aufgegeben und eine ähnliche Lösung wie Sie verwendet.
Richard Le Mesurier
7

Ich würde in Betracht ziehen, die Protokollierungsfunktion von roboguice zu verwenden anstelle des integrierten android.util.Log zu verwenden

Ihre Funktion deaktiviert automatisch Debug- und ausführliche Protokolle für Release-Builds. Außerdem erhalten Sie einige nützliche Funktionen kostenlos (z. B. anpassbares Protokollierungsverhalten, zusätzliche Daten für jedes Protokoll und mehr).

Mit proguard ziemlich lästig sein könnte und ich würde die Mühe der Konfiguration und macht es nicht durchgehen arbeitet mit Ihrer Anwendung , wenn Sie einen guten Grund dafür haben (Deaktivierung Protokolle ist nicht gut)

Zvi
quelle
Ein sehr schöner Ansatz, wenn Sie die Verschleierung nicht verwenden können ... besonders, weil Roboguice wegen Proguard LOL
Snicolas
1
Aktualisierter Link für die Protokollierungsfunktion von robojuice: github.com/roboguice/roboguice/wiki/Logging-via-Ln
RenniePet
7

Ich poste diese Lösung, die speziell für Android Studio-Benutzer gilt. Ich habe kürzlich auch Timber entdeckt und es erfolgreich in meine App importiert, indem ich Folgendes getan habe:

Fügen Sie die neueste Version der Bibliothek in Ihr build.gradle ein:

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

Gehen Sie dann in Android Studios zu Bearbeiten -> Suchen -> In Pfad ersetzen ...

Geben Log.e(TAG,Sie in das "Text to find"Textfeld ein oder wie auch immer Sie Ihre Protokollnachrichten definiert haben . Dann ersetzen Sie es einfach durchTimber.e(

Geben Sie hier die Bildbeschreibung ein

Klicken Sie auf Suchen und ersetzen Sie alle.

Android Studios durchsucht nun alle Ihre Dateien in Ihrem Projekt und ersetzt alle Protokolle durch Timbers.

Das einzige Problem, das ich mit dieser Methode hatte, ist, dass gradle danach eine Million Fehlermeldungen ausgibt, weil es nicht "Timber" in den Importen für jede Ihrer Java-Dateien finden kann. Klicken Sie einfach auf die Fehler und Android Studios importiert automatisch "Timber" in Ihr Java. Sobald Sie dies für alle Ihre Fehlerdateien getan haben, wird gradle erneut kompiliert.

Sie müssen diesen Code auch in Ihre onCreateMethode Ihrer ApplicationKlasse einfügen :

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

Dies führt nur dann zur App-Protokollierung, wenn Sie sich im Entwicklungsmodus befinden und nicht in der Produktion. Sie können sich auch BuildConfig.RELEASEim Release-Modus anmelden.

Simon
quelle
3
Versuchen Sie, dasselbe für Ihre Importe zu tun, und stellen Sie sicher, dass das Kontrollkästchen Regulärer Ausdruck aktiviert ist. Zu suchender Text: import android\.util\.Log\;Ersetzen durch:import android\.util\.Log\;\nimport timber\.log\.Timber\;
Clark Wilson
oder Sie können strukturelle Suche und Ersetzung verwenden, wie Chike Mgbemena in seinem Beitrag
Maksim Turaev
@MaksimTuraev Dein Link ist nicht mehr relevant. Jetzt ist es ein Blog über Frisuren.
Vadim Kotov
Es sieht so aus, als ob der Beitrag entfernt wurde = (kann ihn nirgendwo finden.
Maksim Turaev
@MaksimTuraev hier ist eine Kopie von Wayback-Maschine, aber die Bilder sind kaputt - web.archive.org/web/20161004161318/http://chikemgbemena.com/…
Vadim Kotov
6

Per android.util.Log bietet eine Möglichkeit zum Aktivieren / Deaktivieren des Protokolls:

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

Standardmäßig gibt die Methode isLoggable (...) false zurück, erst nachdem Sie setprop im Gerät wie folgt eingestellt haben:

adb shell setprop log.tag.MyAppTag DEBUG

Dies bedeutet, dass jedes Protokoll über der DEBUG-Ebene ausgedruckt werden kann. Referenz Android Doc:

Überprüft, ob ein Protokoll für das angegebene Tag auf der angegebenen Ebene protokollierbar ist. Die Standardstufe eines Tags ist INFO. Dies bedeutet, dass alle Ebenen über und einschließlich INFO protokolliert werden. Bevor Sie eine Protokollierungsmethode aufrufen, sollten Sie überprüfen, ob Ihr Tag protokolliert werden soll. Sie können die Standardstufe ändern, indem Sie eine Systemeigenschaft festlegen: 'setprop log.tag. 'Wo Level entweder VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT oder SUPPRESS ist. SUPPRESS deaktiviert die gesamte Protokollierung für Ihr Tag. Sie können auch eine local.prop-Datei erstellen, die Folgendes enthält: 'log.tag. =' Und diese in /data/local.prop ablegen.

Wir könnten also ein benutzerdefiniertes Protokoll verwenden:

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);
    }
}
Richard
quelle
6

Wenn Sie ein globales Ersetzen (einmal) ausführen und danach einige Codierungskonventionen beibehalten können, können Sie dem im Android- Framework häufig verwendeten Muster folgen .

Anstatt zu schreiben

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

habe es als

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

Jetzt kann proguard den StringBuilder und alle Strings und Methoden, die er unterwegs verwendet, aus dem optimierten Release-DEX entfernen. Verwenden proguard-android-optimize.txtSie und Sie müssen sich keine Sorgen um android.util.Log in Ihrem proguard-rules.pro:

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

Mit dem Android Studio Gradle Plugin ist es ziemlich zuverlässig, sodass Sie keine zusätzlichen Konstanten benötigen, um das Strippen zu steuern.BuildConfig.DEBUG

Alex Cohn
quelle
4

Fügen Sie Ihrer Datei proguard-rules.txt Folgendes hinzu

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

Geben Sie hier die Bildbeschreibung ein

Das habe ich bei meinen Android-Projekten gemacht.

In Android Studio können wir ähnliche Vorgänge ausführen, indem wir Strg + Umschalt + F aus dem gesamten Projekt suchen (Befehl + Umschalt + F in MacOs) und Strg + Umschalt + R ersetzen ((Befehl + Umschalt + R in MacOs)).

Lins Louis
quelle
Dies scheint die Arbeit mit Eclipse-Projekten zu eröffnen. Die Suchoption ist nicht einmal in Android-Studios verfügbar.
Simon
2
In Android Studio können Sie eine ähnliche Suche mit Strg + Umschalt + F-Verknüpfung durchführen
Lins Louis
Der Beispielcode in der Frage erklärt, warum dies nicht zuverlässig ist.
Nicolas Raoul
Dies kann zu Problemen beim Entfernen von Befehlen führen, die in Log enthalten sind. Zum BeispielocolateLog.recipie ();
Andrew S
Diese Option für Android Studio 2.1 konnte nicht gefunden werden. Ich kann diesen Trick auch für 1 Datei gleichzeitig durch normales Suchen / Ersetzen verwenden.
VVB
3

Ich habe eine sehr einfache Lösung. Ich verwende IntelliJ für die Entwicklung, daher variieren die Details, aber die Idee sollte für alle IDEs gelten.

Ich wähle das Stammverzeichnis meines Quellbaums aus, klicke mit der rechten Maustaste und wähle "Ersetzen" aus. Ich beschließe dann, alle "Log" zu ersetzen. mit "// Log.". Dadurch werden alle Protokollanweisungen entfernt. Um sie später wieder zurückzusetzen, wiederhole ich das gleiche Ersetzen, diesmal jedoch als Ersetzen aller "// Log". mit "Log.".

Funktioniert einfach super für mich. Denken Sie daran, den Ersatz auf Groß- und Kleinschreibung zu setzen, um Unfälle wie "Dialog" zu vermeiden. Für zusätzliche Sicherheit können Sie auch den ersten Schritt mit "Protokoll" ausführen. als zu suchende Zeichenfolge.

Brillant.

kg_sYy
quelle
2
Bitte lesen Sie den Abschnitt "Wenn ich die Protokollzeile kommentiere" in meiner Frage.
Nicolas Raoul
OK, ja, ich sollte nach dem Durchsuchen der Antworten öfter noch einmal lesen :). Wenn Sie solche Fälle haben, möchten Sie möglicherweise eine andere Lösung, wie sie zuvor vorgeschlagen wurde, z. B. alle Ihre Protokolle hinter einer anderen Schnittstelle abzulegen. Mein Vorschlag funktioniert vielleicht besser für kleinere Teams und Projekte, bei denen die Leute den Aufwand für zusätzliche Protokollierungsbibliotheken vermeiden möchten, die Leute und den Code gut kennen usw.
kg_sYy
1
Das Ersetzen von Log.d durch; // Log.d kümmert sich auch um das "If" -Szenario.
Jasper
3

Wie aus dem Kommentar von zserge hervorgeht ,

Timber ist sehr schön, aber wenn Sie bereits ein Projekt haben, können Sie github.com/zserge/log ausprobieren. Es ist ein Ersatz für android.util.Log und bietet die meisten Funktionen von Timber und noch mehr.

seine Logbibliothek bietet einen einfachen Schalter zum Aktivieren / Deaktivieren des Protokolldrucks (siehe unten).

Außerdem müssen nur die importZeilen geändert werden, und für die Anweisung muss nichts geändert Log.d(...);werden.

if (!BuildConfig.DEBUG)
    Log.usePrinter(Log.ANDROID, false); // from now on Log.d etc do nothing and is likely to be optimized with JIT
Youngjae
quelle
Müssen Sie diese Codezeile in jede Aktivität / jedes Fragment oder nur an einer Stelle einfügen?
Noah Ternullo
@NoahTernullo // in abgeleiteter Anwendungsdatei. DefaultApplication.java
Youngjae
1

Ich habe die obige Lösung verbessert, indem ich Unterstützung für verschiedene Protokollebenen bereitgestellt und die Protokollebenen automatisch geändert habe, je nachdem, ob der Code auf einem Live-Gerät oder auf dem Emulator ausgeführt wird.

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


}
danwms
quelle
1
Gleiches Problem wie bei der vorherigen Lösung. Wenn der Zeichenfolgenparameter mit teuren Aufrufen erstellt wird, werden immer noch Ressourcen verschwendet. Die Überprüfung auf Aufruf muss durchgeführt werden, bevor die übergebenen Parameter erstellt werden.
Typ-a1pha
1

ProGuard erledigt dies für Sie bei Ihrem Release-Build und jetzt für die guten Nachrichten von android.com:

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

Das ProGuard-Tool verkleinert, optimiert und verschleiert Ihren Code, indem nicht verwendeter Code entfernt und Klassen, Felder und Methoden mit semantisch undurchsichtigen Namen umbenannt werden. Das Ergebnis ist eine kleinere APK-Datei, deren Reverse Engineering schwieriger ist. Da ProGuard das Reverse Engineering Ihrer Anwendung erschwert, ist es wichtig, dass Sie es verwenden, wenn Ihre Anwendung sicherheitsrelevante Funktionen verwendet, z. B. wenn Sie Ihre Anwendungen lizenzieren.

ProGuard ist in das Android-Build-System integriert, sodass Sie es nicht manuell aufrufen müssen. ProGuard wird nur ausgeführt, wenn Sie Ihre Anwendung im Release-Modus erstellen, sodass Sie sich beim Erstellen Ihrer Anwendung im Debug-Modus nicht mit verschleiertem Code befassen müssen. Das Ausführen von ProGuard ist völlig optional, wird jedoch dringend empfohlen.

In diesem Dokument wird beschrieben, wie Sie ProGuard aktivieren und konfigurieren sowie das Retrace-Tool zum Dekodieren verschleierter Stapelspuren verwenden

Max Gold
quelle
2
Es scheint jedoch nicht standardmäßig die Debug-Protokollierung zu entfernen. Christophers Antwort klingt also besser.
Nicolas Raoul
0

Ich verwende gerne Log.d (TAG, ein String, oft ein String.format ()).

TAG ist immer der Klassenname

Transformiere Log.d (TAG, -> Logd (im Text deiner Klasse)

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

Auf diese Weise setzen Sie MainClass.debug auf false, wenn Sie bereit sind, eine Release-Version zu erstellen!

user462990
quelle
1
Das Problem mit dieser und anderen Lösungen, abgesehen von Proguard oder Kommentaren, ist, dass Sie im Code belassen, was möglicherweise eine große Anzahl von String-Builds verursacht. in einer durchschnittlichen App kein Problem, aber wenn Sie versuchen, zu optimieren, wird es ein Problem.
Lassi Kinnunen
0

Protokolle können mit bash unter Linux und sed entfernt werden:

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

Funktioniert für mehrzeilige Protokolle. In dieser Lösung können Sie sicher sein, dass keine Protokolle im Produktionscode vorhanden sind.

Sylwano
quelle
0

Ich weiß, dass dies eine alte Frage ist, aber warum haben Sie nicht alle Ihre Protokollaufrufe durch etwas wie Boolean logCallWasHere = true ersetzt? // --- Rest deines Logs hier

Aus diesem Grund wissen Sie, wann Sie sie zurücksetzen möchten, und sie wirken sich nicht auf Ihren if-Anweisungsaufruf aus :)

masood elsad
quelle
Interessant, hoffentlich werden solche Zeilen dann vom Compiler / Optimierer ignoriert. Der Variablenname muss jedoch eindeutig sein, da einige Methoden mehrere Protokollaufrufe haben und Sie dieselbe Variable nicht zweimal deklarieren können.
Nicolas Raoul
Sie können die Variable oben bei Aktivität deklarieren und die boolesche Deklaration aus dieser Zeile entfernen;)
masood elsad
0

Warum nicht einfach tun

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

? Es werden keine zusätzlichen Bibliotheken benötigt, keine Proguard-Regeln, die dazu neigen, das Projekt zu vermasseln, und der Java-Compiler lässt nur den Bytecode für diesen Aufruf aus, wenn Sie einen Release-Build durchführen.

Primož Kralj
quelle
Unpraktisch ist, dass es ausführlicher ist als nur zu schreiben Log.d("tag","msg");, und es ist auch leicht zu vergessen, den if(BuildConfig.DEBUG)Teil zu schreiben .
Nicolas Raoul
1
Ein weiteres Problem dabei ist, dass die Zeichenfolgen in der gepackten Version verbleiben.
Straya
0

Hier ist meine Lösung, wenn Sie nicht mit zusätzlichen Bibliotheken herumspielen oder Ihren Code manuell bearbeiten möchten. Ich habe dieses Jupyter-Notizbuch erstellt , um alle Java-Dateien zu durchsuchen und alle Protokollnachrichten zu kommentieren. Nicht perfekt, aber es hat den Job für mich erledigt.

Naci
quelle
0

auf meine Art:

1) Aktivieren Sie den Spaltenauswahlmodus (Alt + Umschalt + Einfügen).

2) Wählen Sie auf einem Log.d (TAG, "Text"); der Teil 'Log'.

3) dann Shift + Strg + Alt + J.

4) Klicken Sie auf den Pfeil nach links

5) Shift + End

6) Klicken Sie auf Löschen.

Dadurch werden alle LOG-Aufrufe in einer Java-Datei gleichzeitig entfernt.

Ronny Bigler
quelle
0

Sie können versuchen, diese einfache konventionelle Methode zu verwenden:

Ctrl+ Shift+R

ersetzen

Log.e(

Mit

// Log.e(
Fathur Rohim
quelle
Das würde mit dem in der Frage angegebenen Beispielcode nicht gut funktionieren.
Nicolas Raoul
0

Einfach mit Kotlin, deklarieren Sie einfach ein paar Top-Level-Funktionen

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)
}
Zakhar Rodionov
quelle
-1

der einfachste Weg;

verwenden DebugLog

Alle Protokolle werden von DebugLog deaktiviert, wenn die App veröffentlicht wird.

https://github.com/MustafaFerhan/DebugLog

Mustafa Ferhan
quelle
Das ist absolut falsch. Dies führt nur dazu, dass die Protokolle nicht protokolliert werden, dass sie nicht aus dem Code entfernt werden. Sie sind also immer noch da, um die Leute bei der Rückentwicklung Ihres Codes zu unterstützen, und es entstehen immer noch die Kosten für die Formatierung der Zeichenfolgen all dieser Protokolle.
Glenn Maynard