Comment activer / désactiver les niveaux de journalisation dans Android?

149

J'ai beaucoup d'instructions de journalisation à déboguer par exemple.

Log.v(TAG, "Message here");
Log.w(TAG, " WARNING HERE");

lors du déploiement de cette application sur le téléphone de l'appareil, je souhaite désactiver la journalisation détaillée à partir de laquelle je peux activer / désactiver la journalisation.

d-man
la source

Réponses:

80

Une méthode courante consiste à créer un int nommé loglevel et à définir son niveau de débogage en fonction du niveau de journalisation.

public static int LOGLEVEL = 2;
public static boolean ERROR = LOGLEVEL > 0;
public static boolean WARN = LOGLEVEL > 1;
...
public static boolean VERBOSE = LOGLEVEL > 4;

    if (VERBOSE) Log.v(TAG, "Message here"); // Won't be shown
    if (WARN) Log.w(TAG, "WARNING HERE");    // Still goes through

Plus tard, vous pouvez simplement changer le LOGLEVEL pour tous les niveaux de sortie de débogage.

Cytown
la source
1
bien, mais comment désactiveriez-vous DEBUG dans votre exemple, tout en affichant toujours des avertissements ....
Andre Bossard
1
Les instructions if ne finiraient-elles pas dans le code d'octet .apk? Je pensais que nous voulions (généralement) désactiver la journalisation lorsque l'application était déployée, mais l'instruction if ne serait pas supprimée.
chessofnerd
2
dans votre exemple, les messages DEBUG seront affichés, contrairement aux WARNs? ne voudriez-vous pas normalement en face?
Sam
15
Utilisez BuildConfig.DEBUG au lieu de variables personnalisées
hB0
1
@chessofnerd "en Java, le code à l'intérieur de if ne fera même pas partie du code compilé. Il doit compiler, mais il ne sera pas écrit dans le bytecode compilé." stackoverflow.com/questions/7122723/…
stoooops
197

La documentation Android dit ce qui suit à propos des niveaux de journal :

Verbose ne doit jamais être compilé dans une application, sauf pendant le développement. Les journaux de débogage sont compilés mais supprimés au moment de l'exécution. Les journaux d'erreur, d'avertissement et d'information sont toujours conservés.

Vous pouvez donc envisager de supprimer le journal des instructions de déconnexion verbeuses , éventuellement en utilisant ProGuard comme suggéré dans une autre réponse .

Selon la documentation, vous pouvez configurer la journalisation sur un périphérique de développement à l'aide des propriétés système. La propriété est à jeu log.tag.<YourTag>et il doit être réglé sur l' une des valeurs suivantes: VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERTou SUPPRESS. Plus d'informations à ce sujet sont disponibles dans la documentation duisLoggable() méthode.

Vous pouvez définir temporairement les propriétés à l'aide de la setpropcommande. Par exemple:

C:\android>adb shell setprop log.tag.MyAppTag WARN
C:\android>adb shell getprop log.tag.MyAppTag
WARN

Vous pouvez également les spécifier dans le fichier '/data/local.prop' comme suit:

log.tag.MyAppTag=WARN

Les versions ultérieures d'Android semblent exiger que /data/local.prop soit en lecture seule . Ce fichier est lu au démarrage, vous devrez donc redémarrer après l'avoir mis à jour. Si/data/local.prop monde est accessible en écriture, il sera probablement ignoré.

Enfin, vous pouvez les définir par programme à l'aide de la System.setProperty()méthode .

Dave Webb
la source
4
J'ai eu la même expérience; la documentation de l'API ne sait pas exactement comment cela devrait fonctionner et semble même mentionner la plupart des android.util.Configconstantes obsolètes. Les valeurs codées en dur spécifiées dans la documentation de l'API sont inutiles car elles varient (supposément) selon la version. Par conséquent, la route ProGuard nous a semblé la meilleure solution.
Christopher Orr
3
Avez-vous eu de la chance pour configurer la journalisation Android à l'aide du fichier /data/local.prop, de la méthode setprop ou avec un System.setProperty? J'ai un peu de mal à obtenir un Log.isLoggable (TAG, VERBOSE) pour revenir vrai pour moi.
seanoshea
2
J'ai fait fonctionner le débogage Android. L'astuce est que lorsque vous appelez quelque chose comme Log.d ("xyz"), le message est écrit dans logcat même si le débogage est désactivé pour l'enregistreur. Cela signifie que le filtrage se produit généralement après avoir été écrit. Afin de filtrer avant quelque chose comme Log.isLoggable (TAG, Log.VERBOSE)) {Log.v (TAG, "my log message"); } est nécessaire. C'est généralement assez ennuyeux. J'utilise une version modifiée de slf4j-android pour obtenir ce que je veux.
phreed
2
@Dave avez-vous pu faire fonctionner correctement la méthode local.prop. Je suis également incapable de faire ce travail, j'ai créé une entrée log.tag.test = INFO, puis j'ai essayé de la changer en exécutant setprop log.tag.test SUPPRESS à partir du shell adb et cela ne change rien. L'utilisation de System.getProperty et System.setProperty ne fait rien. Je voulais recevoir une mise à jour de votre part. Merci.
jjNford le
2
+1 pour le commentaire "les documents de l'API ne sont pas très clairs sur la façon dont cela devrait fonctionner".
Alan
90

Le moyen le plus simple est probablement d'exécuter votre JAR compilé via ProGuard avant le déploiement, avec une configuration comme:

-assumenosideeffects class android.util.Log {
    public static int v(...);
}

Cela supprimera - à part toutes les autres optimisations de ProGuard - toutes les instructions de journal détaillées directement du bytecode.

Christophe Orr
la source
do it contient n'importe quel fichier log.property dans lequel nous pouvons définir les paramètres.
d-man
1
supprimer des lignes avec proguard signifie que vos traces de pile de production peuvent ne pas correspondre à votre code.
larham1
3
@ larham1: ProGuard agit sur le bytecode, donc j'imagine que la suppression des appels de journalisation ne modifierait pas les métadonnées du numéro de ligne intégré.
Christopher Orr
19
Attention, même si l'appel réel à Log.v () est supprimé, ses arguments sont toujours évalués. Donc, si vous avez un appel de méthode coûteux à l'intérieur, par exemple Log.v (TAG, generateLog ()), cela peut nuire à vos performances s'il se trouve dans un chemin de code actif. Même des choses comme toString () ou String.format () peuvent être importantes.
Błażej Czapp
4
@GaneshKrishnan Non, ce n'est pas vrai. L'appel à Log.v () est supprimé mais, par défaut, les appels de méthode pour créer la chaîne ne seront pas supprimés. Voir cette réponse de l'auteur de ProGuard: stackoverflow.com/a/6023505/234938
Christopher Orr
18

J'ai pris une route simple - créer une classe wrapper qui utilise également des listes de paramètres variables.

 public class Log{
        public static int LEVEL = android.util.Log.WARN;


    static public void d(String tag, String msgFormat, Object...args)
    {
        if (LEVEL<=android.util.Log.DEBUG)
        {
            android.util.Log.d(tag, String.format(msgFormat, args));
        }
    }

    static public void d(String tag, Throwable t, String msgFormat, Object...args)
    {
        if (LEVEL<=android.util.Log.DEBUG)
        {
            android.util.Log.d(tag, String.format(msgFormat, args), t);
        }
    }

    //...other level logging functions snipped
kdahlhaus
la source
1
Comme je l'ai mentionné ci-dessus. J'ai utilisé une version modifiée de slf4j-android pour implémenter cette technique.
phreed
3
Il y a un gros problème à ce sujet, voir stackoverflow.com/questions/2446248/...
OneWorld
10

Le meilleur moyen est d'utiliser l'API SLF4J + une partie de son implémentation.

Pour les applications Android, vous pouvez utiliser les éléments suivants:

  1. Enregistreur Android est l'implémentation SLF4J légère mais facile à configurer (<50 Ko).
  2. LOGBack est l'implémentation la plus puissante et la plus optimisée, mais sa taille est d'environ 1 Mo.
  3. Tout autre à votre goût: slf4j-android, slf4android.
Fortess Nsk
la source
2
Sur Android, vous devrez utiliser logback-android(car le logbackbon est incompatible). logback-android-1.0.10-1.jarest de 429 Ko, ce qui n'est pas trop mal compte tenu des fonctionnalités fournies, mais la plupart des développeurs utiliseraient de toute façon Proguard pour optimiser leur application.
tony19
Cela ne vous évite pas d'utiliser des instructions if pour vérifier le niveau de journalisation avant la journalisation. Voir stackoverflow.com/questions/4958860/…
OneWorld
8

Tu devrais utiliser

    if (Log.isLoggable(TAG, Log.VERBOSE)) {
        Log.v(TAG, "my log message");
    }
Diego Torres Milan
la source
2
Comment configurer la sortie d'isLoggable? Le débogage et le verbeux ne sont-ils pas journalisables lorsque isDebugable est défini sur false dans le manifeste?
OneWorld
5

Suppression de la journalisation avec proguard (voir la réponse de @Christopher) était facile et rapide, mais cela faisait que les traces de pile de la production ne correspondaient pas à la source s'il y avait une journalisation de débogage dans le fichier.

Au lieu de cela, voici une technique qui utilise différents niveaux de journalisation dans le développement par rapport à la production, en supposant que proguard est utilisé uniquement en production. Il reconnaît la production en voyant si proguard a renommé un nom de classe donné (dans l'exemple, j'utilise "com.foo.Bar" - vous le remplaceriez par un nom de classe complet dont vous savez qu'il sera renommé par proguard).

Cette technique utilise la journalisation commune.

private void initLogging() {
    Level level = Level.WARNING;
    try {
        // in production, the shrinker/obfuscator proguard will change the
        // name of this class (and many others) so in development, this
        // class WILL exist as named, and we will have debug level
        Class.forName("com.foo.Bar");
        level = Level.FINE;
    } catch (Throwable t) {
        // no problem, we are in production mode
    }
    Handler[] handlers = Logger.getLogger("").getHandlers();
    for (Handler handler : handlers) {
        Log.d("log init", "handler: " + handler.getClass().getName());
        handler.setLevel(level);
    }
}
larham1
la source
3

Il existe un petit remplacement pour la classe standard Android Log - https://github.com/zserge/log

En gros, tout ce que vous avez à faire est de remplacer les importations de android.util.Logà trikita.log.Log. Ensuite, dans votre Application.onCreate()ou dans un initalizer statique, vérifiez le BuilConfig.DEBUGou tout autre indicateur et utilisez Log.level(Log.D)ou Log.level(Log.E)pour modifier le niveau de journalisation minimal. Vous pouvez utiliser Log.useLog(false)pour désactiver la journalisation.

Eric Weyant
la source
2

Vous pouvez peut-être voir cette classe d'extension de journal: https://github.com/dbauduin/Android-Tools/tree/master/logs .

Il vous permet d'avoir un contrôle fin sur les logs. Vous pouvez par exemple désactiver tous les journaux ou uniquement les journaux de certains packages ou classes.

De plus, il ajoute des fonctionnalités utiles (par exemple, vous n'avez pas à passer une balise pour chaque journal).

Âne
la source
2

J'ai créé un utilitaire / wrapper qui résout ce problème + d'autres problèmes courants autour de la journalisation.

Un utilitaire de débogage avec les fonctionnalités suivantes:

  • Les fonctionnalités habituelles fournies par la classe Log enveloppée par LogMode s.
  • Journaux d'entrée-sortie de méthode: peuvent être désactivés par un interrupteur
  • Débogage sélectif: débogage des classes spécifiques.
  • Mesure du temps d'exécution de la méthode: mesure le temps d'exécution des méthodes individuelles ainsi que le temps collectif consacré à toutes les méthodes d'une classe.

Comment utiliser?

  • Incluez la classe dans votre projet.
  • Utilisez-le comme vous utilisez les méthodes android.util.Log, pour commencer.
  • Utilisez la fonction de journaux d'entrée-sortie en appelant les méthodes entry_log () - exit_log () au début et à la fin des méthodes de votre application.

J'ai essayé de rendre la documentation autonome.

Les suggestions pour améliorer cet utilitaire sont les bienvenues.

Libre à utiliser / partager.

Téléchargez-le depuis GitHub .

Vinay W
la source
2

Voici une solution plus complexe. Vous obtiendrez une trace complète de la pile et la méthode toString () ne sera appelée que si nécessaire (Performance). L'attribut BuildConfig.DEBUG aura la valeur false en mode production afin que tous les journaux de trace et de débogage soient supprimés. Le compilateur de points chauds a la possibilité de supprimer les appels car les propriétés statiques finales sont désactivées.

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import android.util.Log;

public class Logger {

    public enum Level {
        error, warn, info, debug, trace
    }

    private static final String DEFAULT_TAG = "Project";

    private static final Level CURRENT_LEVEL = BuildConfig.DEBUG ? Level.trace : Level.info;

    private static boolean isEnabled(Level l) {
        return CURRENT_LEVEL.compareTo(l) >= 0;
    }

    static {
        Log.i(DEFAULT_TAG, "log level: " + CURRENT_LEVEL.name());
    }

    private String classname = DEFAULT_TAG;

    public void setClassName(Class<?> c) {
        classname = c.getSimpleName();
    }

    public String getClassname() {
        return classname;
    }

    public boolean isError() {
        return isEnabled(Level.error);
    }

    public boolean isWarn() {
        return isEnabled(Level.warn);
    }

    public boolean isInfo() {
        return isEnabled(Level.info);
    }

    public boolean isDebug() {
        return isEnabled(Level.debug);
    }

    public boolean isTrace() {
        return isEnabled(Level.trace);
    }

    public void error(Object... args) {
        if (isError()) Log.e(buildTag(), build(args));
    }

    public void warn(Object... args) {
        if (isWarn()) Log.w(buildTag(), build(args));
    }

    public void info(Object... args) {
        if (isInfo()) Log.i(buildTag(), build(args));
    }

    public void debug(Object... args) {
        if (isDebug()) Log.d(buildTag(), build(args));
    }

    public void trace(Object... args) {
        if (isTrace()) Log.v(buildTag(), build(args));
    }

    public void error(String msg, Throwable t) {
        if (isError()) error(buildTag(), msg, stackToString(t));
    }

    public void warn(String msg, Throwable t) {
        if (isWarn()) warn(buildTag(), msg, stackToString(t));
    }

    public void info(String msg, Throwable t) {
        if (isInfo()) info(buildTag(), msg, stackToString(t));
    }

    public void debug(String msg, Throwable t) {
        if (isDebug()) debug(buildTag(), msg, stackToString(t));
    }

    public void trace(String msg, Throwable t) {
        if (isTrace()) trace(buildTag(), msg, stackToString(t));
    }

    private String buildTag() {
        String tag ;
        if (BuildConfig.DEBUG) {
            StringBuilder b = new StringBuilder(20);
            b.append(getClassname());

            StackTraceElement stackEntry = Thread.currentThread().getStackTrace()[4];
            if (stackEntry != null) {
                b.append('.');
                b.append(stackEntry.getMethodName());
                b.append(':');
                b.append(stackEntry.getLineNumber());
            }
            tag = b.toString();
        } else {
            tag = DEFAULT_TAG;
        }
    }

    private String build(Object... args) {
        if (args == null) {
            return "null";
        } else {
            StringBuilder b = new StringBuilder(args.length * 10);
            for (Object arg : args) {
                if (arg == null) {
                    b.append("null");
                } else {
                    b.append(arg);
                }
            }
            return b.toString();
        }
    }

    private String stackToString(Throwable t) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(500);
        baos.toString();
        t.printStackTrace(new PrintStream(baos));
        return baos.toString();
    }
}

utiliser comme ceci:

Loggor log = new Logger();
Map foo = ...
List bar = ...
log.error("Foo:", foo, "bar:", bar);
// bad example (avoid something like this)
// log.error("Foo:" + " foo.toString() + "bar:" + bar); 
Andreas Mager
la source
1

Dans un scénario de journalisation très simple, où vous essayez littéralement d'écrire sur la console pendant le développement à des fins de débogage, il peut être plus facile de simplement faire une recherche et un remplacement avant votre génération de production et commenter tous les appels à Log ou System. out.println.

Par exemple, en supposant que vous n'ayez pas utilisé le "Journal". n'importe où en dehors d'un appel à Log.d ou Log.e, etc., vous pouvez simplement effectuer une recherche et un remplacement sur l'ensemble de la solution pour remplacer «Log». avec "// Log." pour commenter tous vos appels de journalisation, ou dans mon cas, j'utilise juste System.out.println partout, donc avant de passer à la production, je vais simplement faire une recherche complète et remplacer "System.out.println" et remplacer par "//System.out.println".

Je sais que ce n'est pas idéal, et ce serait bien si la possibilité de trouver et de commenter les appels à Log et System.out.println était intégrée à Eclipse, mais jusqu'à ce que cela se produise, le moyen le plus simple et le plus rapide de le faire est pour commenter par recherche et remplacement. Si vous faites cela, vous n'avez pas à vous soucier de la non-concordance des numéros de ligne de trace de pile, car vous éditez votre code source et vous n'ajoutez aucune surcharge en vérifiant une configuration de niveau de journal, etc.

Jim
la source
1

Dans mes applications, j'ai une classe qui encapsule la classe Log qui a un var booléen statique appelé "state". Tout au long de mon code, je vérifie la valeur de la variable "état" en utilisant une méthode statique avant d'écrire réellement dans le journal. J'ai ensuite une méthode statique pour définir la variable "état" qui garantit que la valeur est commune à toutes les instances créées par l'application. Cela signifie que je peux activer ou désactiver toute la journalisation de l'application en un seul appel, même lorsque l'application est en cours d'exécution. Utile pour les appels de support ... Cela signifie que vous devez vous en tenir à vos armes lors du débogage et ne pas reculer pour utiliser la classe Log standard cependant ...

Il est également utile (pratique) que Java interprète une variable booléenne comme fausse si aucune valeur ne lui a été attribuée, ce qui signifie qu'elle peut rester fausse jusqu'à ce que vous deviez activer la journalisation :-)

Darnst
la source
1

Nous pouvons utiliser la classe Logdans notre composant local et définir les méthodes comme v / i / e / d. Basé sur le besoin de nous pouvons faire appel plus loin.
l'exemple est montré ci-dessous.

    public class Log{
        private static boolean TAG = false;
        public static void d(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.d(enable_tag, message+args);
        }
        public static void e(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.e(enable_tag, message+args);
        }
        public static void v(String enable_tag, String message,Object...args){
            if(TAG)
            android.util.Log.v(enable_tag, message+args);
        }
    }
    if we do not need any print(s), at-all make TAG as false for all else 
    remove the check for type of Log (say Log.d).
    as 
    public static void i(String enable_tag, String message,Object...args){
    //      if(TAG)
            android.util.Log.i(enable_tag, message+args);
    }

ici le message est pour stringet et argsest la valeur que vous souhaitez imprimer.

Gyanendra Tripathi
la source
0

Pour moi, il est souvent utile de pouvoir définir différents niveaux de journalisation pour chaque TAG.

J'utilise cette classe wrapper très simple:

public class Log2 {

    public enum LogLevels {
        VERBOSE(android.util.Log.VERBOSE), DEBUG(android.util.Log.DEBUG), INFO(android.util.Log.INFO), WARN(
                android.util.Log.WARN), ERROR(android.util.Log.ERROR);

        int level;

        private LogLevels(int logLevel) {
            level = logLevel;
        }

        public int getLevel() {
            return level;
        }
    };

    static private HashMap<String, Integer> logLevels = new HashMap<String, Integer>();

    public static void setLogLevel(String tag, LogLevels level) {
        logLevels.put(tag, level.getLevel());
    }

    public static int v(String tag, String msg) {
        return Log2.v(tag, msg, null);
    }

    public static int v(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.VERBOSE) {
                return -1;
            }
        }
        return Log.v(tag, msg, tr);
    }

    public static int d(String tag, String msg) {
        return Log2.d(tag, msg, null);
    }

    public static int d(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.DEBUG) {
                return -1;
            }
        }
        return Log.d(tag, msg);
    }

    public static int i(String tag, String msg) {
        return Log2.i(tag, msg, null);
    }

    public static int i(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.INFO) {
                return -1;
            }
        }
        return Log.i(tag, msg);
    }

    public static int w(String tag, String msg) {
        return Log2.w(tag, msg, null);
    }

    public static int w(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.WARN) {
                return -1;
            }
        }
        return Log.w(tag, msg, tr);
    }

    public static int e(String tag, String msg) {
        return Log2.e(tag, msg, null);
    }

    public static int e(String tag, String msg, Throwable tr) {
        if (logLevels.containsKey(tag)) {
            if (logLevels.get(tag) > android.util.Log.ERROR) {
                return -1;
            }
        }
        return Log.e(tag, msg, tr);
    }

}

Maintenant, définissez simplement le niveau de journalisation par TAG au début de chaque classe:

Log2.setLogLevel(TAG, LogLevels.INFO);
Jack Miller
la source
0

Une autre façon consiste à utiliser une plate-forme de journalisation qui a les capacités d'ouvrir et de fermer les journaux. Cela peut parfois donner beaucoup de flexibilité, même sur une application de production, quels journaux doivent être ouverts et lesquels sont fermés en fonction des problèmes que vous rencontrez, par exemple:

Elisha Sterngold
la source