webentwicklung-frage-antwort-db.com.de

So entfernen Sie alle Debug-Protokollierungsaufrufe, bevor Sie die Release-Version einer Android App erstellen?

Laut Google muss ich " alle Aufrufe von Log-Methoden im Quellcode deaktivieren ", bevor ich meine Android App veröffentliche in Google Play. 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 die Aufrufe der Protokollmethoden in Ihren Quelldateien entfernen.

Mein Open-Source-Projekt ist groß und es ist mühsam, es bei jeder Veröffentlichung manuell durchzuführen. 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 so selten, dass ich entscheiden kann, dass sie nicht existieren sollten?

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

371
Nicolas Raoul

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

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 bei Bedarf automatisch kurze Methoden einbinden.

Zum Beispiel ist hier 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 und dann ProGuard von Ant aus anrufen und dabei Ihre gerade kompilierte JAR und die von Ihnen verwendete Plattform-JAR Android) übergeben.

Siehe auch die Beispiele im ProGuard-Handbuch.


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

Dies ist nicht nur ein bisschen besser als die Standardimplementierung von Log - das Protokoll-Tag wird automatisch festgelegt und formatierte Zeichenfolgen und Ausnahmen können problemlos protokolliert werden. Sie können auch zur Laufzeit unterschiedliche Protokollierungsverhalten festlegen.

In diesem Beispiel werden Protokollierungsanweisungen nur in Debugbuilds meiner App in logcat geschrieben:

Timber wird in meiner ApplicationonCreate() -Methode eingerichtet:

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

Dann kann ich mich an einer anderen Stelle in meinem Code problemlos 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 ausführlicheres 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.

464
Christopher Orr

Alles gute Antworten, aber als ich mit meiner Entwicklung fertig war, wollte ich weder if-Anweisungen um alle Log-Aufrufe verwenden, noch 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 = false;

    public static void i(String tag, String string) {
        if (LOG) Android.util.Log.i(tag, string);
    }
    public static void e(String tag, String string) {
        if (LOG) Android.util.Log.e(tag, string);
    }
    public static void d(String tag, String string) {
        if (LOG) Android.util.Log.d(tag, string);
    }
    public static void v(String tag, String string) {
        if (LOG) Android.util.Log.v(tag, string);
    }
    public static void w(String tag, String string) {
        if (LOG) Android.util.Log.w(tag, string);
    }
}

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

112
Reiner

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

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

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

Wenn Sie nun 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 zum Kompilierungszeitpunkt, dass kein Code verwendet wird.)

Bei größeren Projekten möchten Sie möglicherweise Boolesche Werte in einzelnen Dateien verwenden, um die Protokollierung dort nach Bedarf problemlos aktivieren oder deaktivieren zu können. 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 + ")");
60
hackbod

Christopher's Proguard-Lösung 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'

Kommentar-Protokolle:

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

30
Nicolas Raoul

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 von proguard-Android-optimize.txt Als Standarddatei 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'
    }
}

Tatsächlich ist in der Standarddatei proguard-Android.txt Die Optimierung mit den beiden Flags deaktiviert:

-dontoptimize
-dontpreverify

Die Datei proguard-Android-optimize.txt Fügt diese Zeilen nicht hinzu, sodass jetzt assumenosideeffects funktionieren kann.

Persönlich verwende ich dann 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 einige Protokollausgaben möchte, kann er Logback für Android) verwenden und die Protokolle aktivieren, sodass die Protokolle in eine Datei oder nach 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 (natürlich nachdem ich die proguard-Android-optimize.txt - Datei aktiviert habe):

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

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, weil BuildConfig.DEBUG ist unzuverlässig . Dann habe ich in meinen Klassen die folgenden.

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

Ich empfehle dringend, Timber von Jake Wharton zu verwenden

https://github.com/JakeWharton/timber

es behebt Ihr Problem mit dem automatischen Aktivieren/Deaktivieren und Hinzufügen von Tag-Klassen

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

8
AndroidGecko

Ich poste diese Lösung, die speziell für Android Studio-Benutzer gilt. Außerdem habe ich Timber kürzlich entdeckt und sie erfolgreich in meine App importiert, indem ich Folgendes ausgeführt 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 -> Im Pfad ersetzen ...

Geben Sie Log.e(TAG, Ein oder wie auch immer Sie Ihre Protokollmeldungen in das Textfeld "Text to find" Eingegeben haben. Dann ersetzen Sie es einfach durch Timber.e(

enter image description here

Klicken Sie auf Suchen und dann auf Alle ersetzen.

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 in den Importen nicht für jede Ihrer Java) - Dateien "Timber" finden kann. Klicken Sie einfach auf die Fehler und Android Studios importiert "Timber" automatisch 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 onCreate -Methode Ihrer Application -Klasse einfügen:

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

Dies führt dazu, dass die App nur protokolliert wird, wenn Sie sich im Entwicklungsmodus befinden und nicht in Produktion sind. Sie können sich auch mit BuildConfig.RELEASE Im Freigabemodus anmelden.

7
Simon

Ich würde in Betracht ziehen, roboguices Protokollierungsfunktion anstelle des integrierten Android.util.Log zu verwenden

Ihre Einrichtung 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).

Die Verwendung von proguard könnte ein ziemlicher Aufwand sein, und ich würde nicht die Mühe machen, es mit Ihrer Anwendung zu konfigurieren , es sei denn, Sie haben einen guten Grund dafür das (das Deaktivieren von Protokollen ist nicht gut)

7
Zvi

Per Android.util.Log können Sie das Protokoll aktivieren/deaktivieren:

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

Standardmäßig gibt die Methode isLoggable (...) nur dann false zurück, wenn Sie setprop in device so eingestellt haben:

adb Shell setprop log.tag.MyAppTag DEBUG

Dies bedeutet, dass alle Protokolle über dem DEBUG-Level ausgedruckt werden können. Referenz Android doc:

Überprüft, ob ein Protokoll für das angegebene Tag auf der angegebenen Ebene protokollierbar ist. Die Standardstufe eines Tags ist auf INFO eingestellt. 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. 'Wobei die Ebene entweder VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT oder SUPPRESS ist. Mit SUPPRESS wird die gesamte Protokollierung für Ihr Tag deaktiviert. Sie können auch eine local.prop-Datei mit folgendem Inhalt erstellen: 'log.tag. =' Und diese in /data/local.prop einfügen.

Wir könnten also das benutzerdefinierte Protokoll util 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);
    }
}
6
Richard

Wenn Sie ein globales Ersetzen (einmalig) ausführen und danach eine Codierungskonvention beibehalten können, können Sie dem in 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.txt und Sie müssen sich nicht um Android.util.Log in Ihrem proguard-rules.pro:

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

Mit Android Studio Gradle Plugin, BuildConfig.DEBUG ist sehr zuverlässig, so dass Sie keine zusätzlichen Konstanten benötigen, um das Strippen zu steuern.

5
Alex Cohn

Ich habe eine sehr einfache Lösung. Ich benutze 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 entscheide mich dann, alle "Log" zu ersetzen. mit "// Log." Dadurch werden alle Protokollanweisungen entfernt. Um sie später zurückzusetzen, wiederhole ich das gleiche Ersetzen, aber diesmal ersetze ich alle "// Log". mit "Log.".

Funktioniert einfach toll für mich. Denken Sie daran, das Ersetzen 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 "Protokollieren" ausführen. als zu suchende Zeichenfolge.

Brillant.

3
kg_sYy

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

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

enter image description here

Das habe ich bei meinen Android Projekten gemacht ..

In Android Studio können wir eine ähnliche Operation ausführen, indem wir Strg + Umschalt + F verwenden, um aus dem gesamten Projekt (Befehl + Umschalt + F in MacOs) und Strg + Umschalt + R zum Ersetzen ((Befehl +) zu suchen Umschalt + R in MacOs))

3
Lins Louis

Als zserges Kommentar vorgeschlagen,

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

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

Außerdem muss nur import Zeilen geändert werden, und nichts muss für Log.d(...); Anweisung geändert 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
2
Youngjae

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

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

Das ProGuard-Tool verkleinert, optimiert und verschleiert Ihren Code, indem es nicht verwendeten Code entfernt und Klassen, Felder und Methoden mit semantisch undurchsichtigen Namen umbenennt. Das Ergebnis ist eine kleinere APK-Datei, deren Rückentwicklung schwieriger ist. Da ProGuard das Reverse Engineering Ihrer Anwendung erschwert, ist es wichtig, dass Sie diese verwenden, wenn Ihre Anwendung sicherheitsrelevante Funktionen verwendet, beispielsweise 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 nicht mit verschleiertem Code befassen müssen Wenn Sie Ihre Anwendung im Debug-Modus erstellen, ist die Ausführung von ProGuard völlig optional, wird jedoch dringend empfohlen.

In diesem Dokument wird beschrieben, wie Sie ProGuard aktivieren und konfigurieren sowie das Retrace-Tool verwenden, um verschleierte Stack-Traces zu decodieren

1
Max Gold

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


}
1
danwms

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

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

0
user462990

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

Aus diesem Grund wissen Sie, wann Sie sie zurücksetzen möchten, und sie haben keinen Einfluss auf Ihren if-Anweisungsaufruf :)

0
masood elsad

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 im Produktionscode keine Protokolle vorhanden sind.

0
sylwano