web-dev-qa-db-fra.com

Comment supprimer tous les appels de journalisation de débogage avant de créer la version finale d'une application Android?

Selon Google, je dois " désactiver tout appel aux méthodes de journalisation dans le code source " avant de publier mon application Android sur Google Play. . Extrait de la section 3 de la liste de contrôle de publication :

Assurez-vous de désactiver la journalisation et de désactiver l'option de débogage avant de créer votre application pour la publication. Vous pouvez désactiver la journalisation en supprimant les appels aux méthodes de journalisation dans vos fichiers source.

Mon projet open-source est volumineux et il est difficile de le faire manuellement à chaque fois que je publie. En outre, la suppression d'une ligne de journal est potentiellement délicate, par exemple:

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

Si je commente la ligne du journal, la condition s'applique à la ligne suivante et il y a des chances que load () ne soit pas appelé. Ces situations sont-elles suffisamment rares pour que je puisse décider que cela ne devrait pas exister?

Alors, y a-t-il une meilleure façon de faire cela au niveau du code source? Ou peut-être une syntaxe intelligente de ProGuard pour supprimer efficacement toutes les lignes du journal, en toute sécurité?

371
Nicolas Raoul

Je trouve une solution beaucoup plus simple, c’est d’oublier toutes les vérifications if et d’utiliser simplement ProGuard pour supprimer les appels de méthode Log.d() ou Log.v() lorsque nous appelons notre cible Ant release.

De cette façon, nous avons toujours les informations de débogage en sortie pour les générations normales et nous n’avons pas à modifier le code pour les versions. ProGuard peut également effectuer plusieurs passages sur le bytecode pour supprimer d'autres instructions non souhaitées, des blocs vides, et peut automatiquement incorporer des méthodes courtes, le cas échéant.

Par exemple, voici une configuration très basique de ProGuard pour Android:

-dontskipnonpubliclibraryclasses
-dontobfuscate
-forceprocessing
-optimizationpasses 5

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

Vous devez donc enregistrer cela dans un fichier, puis appeler ProGuard depuis Ant, en transmettant votre fichier JAR récemment compilé et le fichier JAR de plate-forme Android que vous utilisez.

Voir aussi les exemples dans le manuel de ProGuard.


Mise à jour (4,5 ans plus tard): De nos jours, j'utilisais Timber pour la journalisation Android.

Non seulement est-elle un peu plus agréable que l'implémentation par défaut de Log - la balise de journal est définie automatiquement, et il est facile de consigner des chaînes et des exceptions formatées - mais vous pouvez également spécifier différents comportements de journalisation lors de l'exécution.

Dans cet exemple, les instructions de journalisation ne seront écrites que dans logcat dans les versions debug de mon application:

Le bois est configuré dans ma méthode ApplicationonCreate():

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

Ensuite, n'importe où dans mon code, je peux me connecter facilement:

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

Voir le exemple d'application de bois pour un exemple plus avancé, dans lequel toutes les instructions de journal sont envoyées à logcat pendant le développement et, en production, aucune instruction de débogage n'est consignée, mais les erreurs sont rapportées en silence à Crashlytics.

464
Christopher Orr

Toutes les réponses sont bonnes, mais lorsque j'ai terminé mon développement, je ne souhaitais plus utiliser les instructions if de tous les appels du journal, ni utiliser d’outils externes.

La solution que j’utilise consiste donc à remplacer la classe Android.util.Log par ma propre classe Log:

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

La seule chose que je devais faire dans tous les fichiers sources était de remplacer l'importation d'Android.util.Log par ma propre classe.

112
Reiner

Je suggère d'avoir un booléen statique quelque part indiquant si vous devez vous connecter ou non:

 class MyDebug {
 statique final booléen LOG = true; 
} 

Ensuite, où que vous souhaitiez vous connecter à votre code, procédez comme suit:

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

Maintenant, lorsque vous définissez MyDebug.LOG sur false, le compilateur supprime tout le code à l'intérieur de ces contrôles (puisqu'il s'agit d'une finale statique, il sait au moment de la compilation que le code n'est pas utilisé.)

Pour les projets plus importants, vous pouvez commencer à avoir des booléens dans des fichiers individuels pour pouvoir facilement activer ou désactiver la journalisation là-bas, si nécessaire. Par exemple, ce sont les différentes constantes de journalisation que nous avons dans le gestionnaire de fenêtres:

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;

Avec le code correspondant comme:

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

La solution Proguard de Christopher est la meilleure, mais si, pour une raison quelconque, vous n'aimez pas Proguard, voici une solution très peu technologique:

Journaux de commentaires:

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

Décommenter les journaux:

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

Une contrainte est que vos instructions de journalisation ne doivent pas s'étendre sur plusieurs lignes.

(Exécutez ces lignes dans un shell UNIX à la racine de votre projet. Si vous utilisez Windows, obtenez un calque UNIX ou utilisez des commandes Windows équivalentes)

30
Nicolas Raoul

Je voudrais ajouter quelques précisions sur l’utilisation de Proguard avec Android Studio et gradle, car j’ai eu beaucoup de problèmes pour supprimer les lignes de journal du fichier binaire final.

Afin de rendre assumenosideeffects dans Proguard, il existe une condition préalable.

Dans votre fichier de classement, vous devez spécifier l'utilisation du fichier proguard-Android-optimize.txt en tant que fichier par défaut.

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

En fait, dans le fichier proguard-Android.txt par défaut, l'optimisation est désactivée avec les deux indicateurs:

-dontoptimize
-dontpreverify

Le fichier proguard-Android-optimize.txt n'ajoute pas ces lignes, donc maintenant assumenosideeffects peut fonctionner.

Ensuite, j'utilise SLF4J , à plus forte raison lorsque je développe des bibliothèques qui sont distribuées à d'autres. L'avantage est que par défaut, il n'y a pas de sortie. Et si l'intégrateur souhaite des sorties de journal, il peut utiliser Logback pour Android et activer les journaux, afin que les journaux puissent être redirigés vers un fichier ou vers LogCat.

Si j'ai vraiment besoin de supprimer les journaux de la bibliothèque finale, j'ajoute alors à mon fichier Proguard (après avoir activé le fichier proguard-Android-optimize.txt bien sûr):

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

J'ai utilisé une classe LogUtils comme dans l'exemple d'application Google IO. J'ai modifié cela pour utiliser une constante DEBUG spécifique à l'application au lieu de BuildConfig.DEBUG car BuildConfig.DEBUG n'est pas fiable . Ensuite, dans mes cours, j'ai ce qui suit.

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

Je suggère fortement d'utiliser du bois de Jake Wharton

https://github.com/JakeWharton/timber

cela résout votre problème d'activation/désactivation et ajoute automatiquement la classe de balises

juste

public class MyApp extends Application {

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

les journaux ne seront utilisés que dans votre version de débogage, puis utilisés.

Timber.d("lol");

ou

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

imprimer

"Votre classe/msg" sans spécifier le tag

8
AndroidGecko

Je publie cette solution qui s'applique spécifiquement à Android utilisateurs de Studio. J'ai aussi récemment découvert Timber et je l'ai importé avec succès dans mon application en procédant comme suit:

Mettez la dernière version de la bibliothèque dans votre build.gradle:

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

Ensuite, dans Android Studios, sélectionnez Edition -> Rechercher -> Remplacer dans le chemin ...

Tapez Log.e(TAG, ou vous avez défini vos messages de journal dans la zone de texte "Text to find". Ensuite, il vous suffit de le remplacer par Timber.e(

enter image description here

Cliquez sur Rechercher, puis remplacez tout.

Android Studios va maintenant parcourir tous vos fichiers de votre projet et remplacer tous les Logs avec Timbers.

Le seul problème que j'ai avec cette méthode est que gradle s'affiche avec un million de messages d'erreur par la suite, car il ne trouve pas "Timber" dans les importations pour chacun de vos fichiers Java. Cliquez simplement sur les erreurs et Android Studios importera automatiquement "Timber" dans votre Java. Une fois que vous l'avez fait pour tous vos fichiers d'erreurs, Gradle compilera à nouveau.

Vous devez également mettre ce morceau de code dans votre méthode onCreate de votre classe Application:

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

Cela entraînera la journalisation de l'application uniquement lorsque vous êtes en mode de développement et non en production. Vous pouvez également avoir BuildConfig.RELEASE pour vous connecter en mode release.

7
Simon

J'envisagerais d'utiliser la fonction de journalisation de Roboguice au lieu d'Android.util.Log intégré

Leur installation désactive automatiquement les journaux de débogage et les journaux détaillés pour les versions de version. De plus, vous bénéficiez gratuitement de fonctionnalités intéressantes (comportement de journalisation personnalisable, données supplémentaires pour chaque journal, etc.).

Utiliser proguard peut être un peu fastidieux et je n’aurais pas eu la peine de configurer et de le faire fonctionner avec votre application, sauf si vous avez une bonne raison de le faire. ça (désactiver les journaux n'est pas un bon)

7
Zvi

Par Android.util.Log fournit un moyen d'activer/désactiver le journal:

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

Par défaut, la méthode isLoggable (...) renvoie false, uniquement après avoir définipropprop dans le périphérique:

adb Shell setprop log.tag.MyAppTag DEBUG

Cela signifie que tout journal au-dessus du niveau DEBUG peut être imprimé. Référence Android doc:

Vérifie si un journal de la balise spécifiée est consignable au niveau spécifié. Le niveau par défaut de toute balise est défini sur INFO. Cela signifie que tout niveau supérieur et incluant INFO sera enregistré. Avant d'appeler une méthode de journalisation, vous devez vérifier si votre tag doit être consigné. Vous pouvez modifier le niveau par défaut en définissant une propriété système: 'setprop log.tag. 'Où level est soit VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT ou SUPPRESS. SUPPRESS désactivera toute la journalisation de votre tag. Vous pouvez également créer un fichier local.prop comportant les éléments suivants: 'log.tag. =' Et placez-le dans /data/local.prop.

Nous pourrions donc utiliser un utilitaire de journalisation personnalisé:

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

Si vous pouvez exécuter un remplacement global (une fois) et que, par la suite, conserver une convention de codage, vous pouvez suivre le modèle souvent utilisé dans Android framework .

Au lieu d'écrire

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

avoir comme

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

Maintenant, proguard peut supprimer le StringBuilder et toutes les chaînes et méthodes qu’il utilise en chemin, de la version optimisée DEX. Utilisez proguard-Android-optimize.txt et vous n'avez pas à vous soucier de Android.util.Log dans votre proguard-rules.pro:

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

Avec Android Studio gradle plugin, BuildConfig.DEBUG est relativement fiable, vous n'avez donc pas besoin de constantes supplémentaires pour contrôler l'effeuillage.

5
Alex Cohn

J'ai une solution très simple. J'utilise IntelliJ pour le développement, donc les détails varient, mais l'idée devrait s'appliquer à tous les environnements de développement intégrés.

Je choisis à la racine de mon arbre source, un clic droit et sélectionnez "Remplacer". J'ai ensuite choisi de remplacer tous les "Journal". avec "// Log.". Cela supprime toutes les instructions du journal. Pour les remettre plus tard, je répète le même remplacement, mais cette fois comme pour remplacer tous les "// Log". avec "Log.".

Ça marche très bien pour moi. Rappelez-vous simplement de définir le remplacement comme sensible à la casse pour éviter les accidents tels que "Dialogue". Pour plus de sécurité, vous pouvez également effectuer la première étape avec "Journal". comme la chaîne à rechercher.

Brillant.

3
kg_sYy

Ajoutez ce qui suit à votre fichier proguard-rules.txt

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

enter image description here

C'est ce que je faisais sur mes Android projets ..

Dans Android Studio, nous pouvons effectuer une opération similaire en appuyant sur Ctrl + Maj + F pour rechercher dans l’ensemble du projet (Commande + Maj + F sous MacOs) et sur Ctrl + Maj + R pour Remplacer ((Commande + Maj + R dans MacOs))

3
Lins Louis

Comme commentaire de zserge suggéré,

Timber is very Nice, mais si vous avez déjà un projet existant, essayez github.com/zserge/log. C'est un remplacement instantané pour Android.util.Log et possède la plupart des fonctionnalités de Timber et bien plus encore.

sa bibliothèque de journaux fournit un commutateur d'activation/désactivation de journal simple comme indiqué ci-dessous.

De plus, il seulement nécessite de changer les lignes import et rien doit changer pour l'instruction Log.d(...);.

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 le fera pour vous lors de la création de votre version et des bonnes nouvelles d'Android.com:

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

L'outil ProGuard réduit, optimise et obscurcit votre code en supprimant le code non utilisé et en renommant les classes, les champs et les méthodes portant des noms sémantiquement obscurs. Le résultat est un fichier .apk de plus petite taille qui est plus difficile à effectuer du reverse engineering. Étant donné que ProGuard rend plus difficile l'ingénierie inverse de votre application, il est important de l'utiliser quand votre application utilise des fonctionnalités sensibles à la sécurité, telles que la licence de vos applications.

ProGuard est intégré au système de construction Android. Vous n'avez donc pas à l'invoquer manuellement. ProGuard ne s'exécute que lorsque vous construisez votre application en mode édition. Vous n'avez donc pas à traiter de code obfusqué lorsque vous créez votre application en mode débogage. L'exécution de ProGuard est totalement facultative, mais vivement recommandée.

Ce document explique comment activer et configurer ProGuard et comment utiliser l'outil de retracement pour décoder les traces de pile obfusquées.

1
Max Gold

J'ai amélioré la solution ci-dessus en prenant en charge différents niveaux de journalisation et en modifiant automatiquement les niveaux de journalisation en fonction du code exécuté sur un périphérique en direct ou sur l'émulateur.

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

J'aime utiliser Log.d (TAG, une chaîne, souvent un String.format ()).

TAG est toujours le nom de la classe

Transform Log.d (TAG, -> Logd (dans le texte de votre classe

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

Ainsi, lorsque vous êtes prêt à créer une version finale, définissez MainClass.debug sur false!

0
user462990

Je sais que c’est une vieille question, mais pourquoi n’avez-vous pas remplacé tous vos appels de journal par quelque chose comme Boolean logCallWasHere = true; // --- reste de votre journal ici

C'est pourquoi vous saurez quand vous souhaitez les remettre, et ils n'affecteront pas votre appel à l'instruction if :)

0
masood elsad

Les journaux peuvent être supprimés en utilisant bash sous linux et sed:

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

Fonctionne pour les journaux multilignes. Dans cette solution, vous pouvez être sûr que les journaux ne sont pas présents dans le code de production.

0
sylwano