Comment étiquetez-vous habituellement les entrées de journal? (Android)

96

Je suppose que la plupart d'entre vous connaissent android.util.Log Toutes les méthodes de journalisation acceptent la «balise String» comme premier argument.

Et ma question est: Comment marquez-vous habituellement vos journaux dans vos applications? J'ai vu un code en dur comme celui-ci:

public class MyActivity extends Activity {
    private static final String TAG = "MyActivity";
    //...
    public void method () {
        //...
        Log.d(TAG, "Some logging");
    }
}

Cela n'a pas l'air sympa pour de nombreuses raisons:

  • Vous pouvez me dire que ce code n'a pas de code en dur, mais c'est le cas.
  • Mon application peut avoir n'importe quel nombre de classes dans différents packages avec le même nom. Il serait donc difficile de lire le journal.
  • Ce n'est pas flexible. Vous avez toujours mis un TAG de champ privé dans votre classe.

Existe-t-il un moyen efficace d'obtenir un TAG pour une classe?

andrii
la source
2
L'utilisation de TAG est suggérée par Android javadoc , donc je ne pense pas que ce soit pire que d'obtenir le nom de la classe au moment de l'exécution
Vladimir
je préfère créer une classe spécifique comme GeneralConstants et mettre mes TAG dessus et je peux atteindre mes balises n'importe quelle classe que je veux comme ça; GeneralConstans.MY_TAG
cagryInside
6
Je pense qu'il est préférable que le TAG soit défini dans la classe, coder en dur le nom de la classe est moche mais c'est la seule façon fiable de travailler avec proguard. Si vous n'utilisez jamais proguard, MyActivity.class.getName () est la meilleure solution. Si vous êtes préoccupé par les noms en double, incluez simplement le nom du package. Avoir des noms de TAG dans un endroit différent deviendra un cauchemar de maintenance.
Ralph Mueller

Réponses:

179

J'utilise un TAG, mais je l'initialise comme ceci:

private static final String TAG = MyActivity.class.getName();

De cette façon, lorsque je refactorise mon code, la balise changera également en conséquence.

gianpi
la source
21
Je définis la constante TAG de la même manière. Cependant, je me demande comment les outils d'obfuscation de code affecteront mes noms de classe et par conséquent la valeur de cette constante?
Gumbit
1
tout ce temps, j'ai collé manuellement "MyActivity.class.getName();". J'ai toujours pensé que "TAG" était juste un espace réservé dans les exemples de Google etc ... pas une vraie Staticvariable! C'est une bien meilleure solution merci :)
wired00
4
Pourquoi ne pas supprimer le statique et l'utiliser à la this.getClass().getName()place pour le rendre plus générique?
theblang
11
Vous pouvez essayer this.getClass (). GetSimpleName () pour éviter les limitations de longueur sur TAG. IllegalArgumentException est levé si le tag.length ()> 23.
Michael Levy
14
Comme mentionné par Ralph Mueller, cette technique ne fonctionne pas si vous utilisez Proguard (comme le font la plupart des projets Android) pour masquer les noms de classe.
John Patterson
16

Je crée généralement une Appclasse qui se trouve dans un package différent et contient des méthodes statiques utiles. Une des méthodes est une getTag()méthode, de cette façon je peux obtenir le TAG partout.
Appla classe ressemble à ceci:

EDIT : Amélioration du commentaire par br mob (Merci :))

public class App {

    public static String getTag() {
        String tag = "";
        final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
        for (int i = 0; i < ste.length; i++) {
            if (ste[i].getMethodName().equals("getTag")) {
                tag = "("+ste[i + 1].getFileName() + ":" + ste[i + 1].getLineNumber()+")";
            }
        }
        return tag;
    }

}

Et quand je veux l'utiliser:

Log.i(App.getTag(), "Your message here");

La sortie de la getTagméthode est le nom de la classe de l'appelant (avec le nom du package), et le numéro de ligne d'où getTagest appelé, pour un débogage facile.

Yaniv
la source
6
Je ne ferais certainement pas ça ... Vos instructions de journal vont avoir un gros impact sur les performances si vous le faites. Si vous faites cela, vous voudrez certainement que proguard supprime les messages de journal pour rien de moins qu'un avertissement sur les versions de production.
Matt Wolfe
1
Matt, tu as absolument raison! C'est une bonne pratique de supprimer / supprimer les journaux en production - stackoverflow.com/a/2019563/2270166
Yaniv
2
Ce n'est probablement plus recommandé car la longueur de la balise est désormais limitée à 23 caractères
Claudio Redi
merci de me montrer comment getStackTrace()fonctionne. Mais je ne l'utiliserai pas parce que c'est cher
BlueWizard
12

Accédez à Android Studio -> préférence -> Modèles en direct -> AndroidLog puis sélectionnez Log.d (TAG, String) .

Dans le texte du modèle remplacer

android.util.Log.d(TAG, "$METHOD_NAME$: $content$");

avec

android.util.Log.d("$className$", "$METHOD_NAME$: $content$");

Image du menu Android

Cliquez ensuite sur Modifier les variables et entrez className () dans la colonne Expression en regard de la colonne className Name .image du menu Android 2

Maintenant, lorsque vous tapez le raccourci, logdil mettra

Log.d("CurrentClassName", "currentMethodName: ");

Vous n'avez plus besoin de définir un TAG.

Nicolas Manzini
la source
1
c'est une utilisation vraiment cool d'Android Studio et une approche intéressante du problème, même si en même temps vous saisissez une chaîne à la place de la variable TAG, ce qui signifie que cela pourrait être un peu fastidieux si nécessaire de le changer, non? +1 pour avoir montré la fonctionnalité, merci!
Voy
3
J'aime cette façon, mais je préfère créer une nouvelle entrée de journal au lieu de modifier celle existante, juste pour être prudent si elle a changé dans une future mise à jour ou quelque chose du genre.
Alaa
9

J'aime améliorer la réponse de Yaniv si vous avez le journal dans ce format (filename.java:XX) xx numéro de ligne, vous pouvez lier le raccourci de la même manière qu'il est lié en cas d'erreur, de cette façon, je peux accéder directement à la ligne en question juste en cliquant sur le logcat

Je mets ceci dans mon application étendue pour pouvoir l'utiliser dans tous les autres fichiers

public static String getTag() {
    String tag = "";
    final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
    for (int i = 0; i < ste.length; i++) {
        if (ste[i].getMethodName().equals("getTag")) {
            tag = "("+ste[i + 1].getFileName() + ":" + ste[i + 1].getLineNumber()+")";
        }
    }
    return tag;
}

Capture d'écran:

br mob
la source
Aimer, "voler" et mettre à jour ma réponse :)
Yaniv
4
Ce n'est probablement plus recommandé car la longueur de la balise est désormais limitée à 23 caractères
Claudio Redi
3

AndroidStudio a un logtmodèle par défaut (vous pouvez taper logtet appuyer sur tab pour qu'il se développe en un sinppet de code). Je recommande de l'utiliser pour éviter de copier-coller la définition de TAG d'une autre classe et d'oublier de changer la classe à laquelle vous faites référence. Le modèle se développe par défaut pour

private static final String TAG = "$CLASS_NAME$"

Pour éviter d'utiliser l'ancien nom de classe après la refactorisation, vous pouvez le remplacer par

private static final String TAG = $CLASS_NAME$.class.getSimpleName();

N'oubliez pas de cocher le bouton "Modifier les variables" et assurez-vous que la CLASS_NAMEvariable est définie pour utiliser l' className()expression et que "Ignorer si défini" est coché.

Hemaolle
la source
2

J'ai créé une classe de variables statiques, de méthodes et de classes nommées S.

Voici la méthode de journalisation:

public static void L(Context ctx, Object s) {
    Log.d("CCC " + ctx.getClass().getName().replace(ctx.getPackageName(), ""), s.toString());
}

Il est appelé dans n'importe quelle classe car S.L(this, whaterver_object);The getClass().getName()ajoute également le nom du package, par conséquent, je le supprime pour éviter de rendre la balise inutilement longue.

Avantages:

  1. Plus court que Log.d(TAG,
  2. Pas besoin de convertir les valeurs int en leur chaîne. En fait pas besoin de tapertoString
  3. N'oubliez pas de supprimer Log.djamais car je dois simplement supprimer la méthode et les emplacements de tous les journaux sont marqués en rouge.
  4. Pas besoin de définir TAG en haut de l'activité car il prend le nom de la classe.
  5. Le TAG a un préfixe de CCC(une chaîne courte et facile à taper) afin qu'il soit facile de lister uniquement vos journaux dans le moniteur Android dans Android Studio. Parfois, vous exécutez simultanément des services ou d'autres classes. Si vous devez rechercher uniquement par nom d'activité, vous ne pouvez pas voir exactement quand une réponse de service a été obtenue, puis une action de votre activité s'est produite. Un préfixe comme CCC aide car il vous donne des journaux chronologiquement avec l'activité dans laquelle il s'est produit
suku
la source
1
Excellente solution! Je l'utilise! Mais j'ai remplacé Context ctxpar Object ctxet ctx.getClass().getName().replace(ctx.getPackageName(), "")par ctx.getClass().getSimpleName(). De cette façon, je peux appeler S.L(Object, Object)n'importe où (y compris dans Fragments qui ne s'étendent pas Context, pour l'instantce).
Antonio Vinicius Menezes Medei
1

Vous pouvez utiliser this.toString()pour obtenir un identifiant unique pour la classe spécifique dans laquelle vous imprimez dans le journal.

kaspermoerch
la source
Cela pourrait coûter cher selon ce qui se toString()passe.
tar
1

Au détriment de la mise à jour de ces chaînes lorsque je déplace du code entre des méthodes ou que je renomme des méthodes, j'aime faire ce qui suit. Philosophiquement, il semble également préférable de conserver «emplacement» ou «contexte» dans la balise, pas dans le message.

public class MyClass {

    // note this is ALWAYS private...subclasses should define their own
    private static final LOG_TAG = MyClass.class.getName();

    public void f() {
        Log.i(LOG_TAG + ".f", "Merry Christmas!");
    }

}

L'avantage ici est que vous pouvez filtrer une seule méthode même si le contenu n'est pas statique, par exemple

Log.i(LOG_TAG + ".f", String.valueOf(new Random().nextInt()));

Le seul inconvénient est que lorsque je renomme f()en g()je dois garder cette chaîne à l'esprit. De plus, le refactoring automatique de l'IDE ne les attrapera pas.

Pendant un moment, j'ai été fan de l'utilisation du nom de classe court, je veux dire LOG_TAG = MyClass.class.getSimpleName(). Je les ai trouvés plus difficiles à filtrer dans les journaux car il y avait moins à faire.

le goudron
la source
1

C'est une question très ancienne, mais même si une réponse mise à jour pour juillet 2018, il est préférable d'utiliser Timber. Afin de consigner correctement la journalisation, les erreurs et les avertissements peuvent être envoyés à des bibliothèques de plantage tierces, telles que Firebase ou Crashlytics.

Dans la classe qui implémente Application, vous devez ajouter ceci:

@Override
public void onCreate() {
    super.onCreate();
    if (BuildConfig.DEBUG) {
        Timber.plant(new Timber.DebugTree());
    } else {
        Timber.plant(new CrashReportingTree());
    }
}

/** A tree which logs important information for crash reporting. */
private static class CrashReportingTree extends Timber.Tree {
    @Override protected void log(int priority, String tag, String message, Throwable t) {
        if (priority == Log.VERBOSE || priority == Log.DEBUG) {
            return;
        }

        FakeCrashLibrary.log(priority, tag, message);

        if (t != null) {
            if (priority == Log.ERROR) {
                FakeCrashLibrary.logError(t);
            } else if (priority == Log.WARN) {
                FakeCrashLibrary.logWarning(t);
            }
        }
    }
}

N'oubliez pas la dépendance du bois.

implementation 'com.jakewharton.timber:timber:4.7.1'
Aleksandrbel
la source
0

Pour les utilisateurs qui visitent cette question:

private val TAG:String = this.javaClass.simpleName;
Pamirzameen
la source
0

ils utilisent Timber pour l'application IOsched 2019 pour afficher les informations de débogage:

implementation 'com.jakewharton.timber:timber:4.7.1'

class ApplicationController: Application() {

override fun onCreate() {  
    super.onCreate()
    if(BuildConfig.DEBUG){
        Timber.plant(Timber.DebugTree())
    }
}   
// enables logs for every activity and service of the application
// needs to be registered in manifest like:  
 <application
    android:label="@string/app_name"
    android:name=".ApplicationController"
    ... >

usage

  Timber.e("Error Message") 
  // will print ->  D/MainActivity: Error Message

  Timber.d("Debug Message");
  Timber.tag("new tag").e("error message");

notez que cela rend les journaux disponibles uniquement pendant l'état DEBUG et vous facilite la tâche de les supprimer manuellement pour le lancement sur Google Play -

lors de la publication de l'application sur le Play Store, nous devons supprimer toutes les instructions de journal de l'application, afin qu'aucune des données d'application telles que les informations utilisateur, les données d'application masquées, les jetons d'authentification ne soient disponibles pour l'utilisateur dans logcat sous forme de texte brut

consultez cet article https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d

Dan Alboteanu
la source
-2

J'utilise généralement le nom de la méthode comme balise mais de Thread

String TAG = Thread.currentThread().getStackTrace()[1].getMethodName();

Cela évite la nouvelle exception.

user2705093
la source
-9
private static final String TAG = new RuntimeException().getStackTrace()[0].getClassName();
Survenir
la source
3
Pourquoi créeriez-vous un nouveau RuntimeExceptionjuste pour obtenir le nom de la classe actuelle? Très mauvais.
asgs
C'est ainsi que je marque mes entrées de journal, c'est la seule solution que je peux correctement refactoriser lorsque je copie une classe d'un projet vers un autre, alors pourquoi pas. Je suis ouvert aux suggestions si vous avez des idées meilleures et plus confortables.
Lever le
1
Si vous copiez simplement des fichiers de classe Java d'un emplacement à un autre, sans aucun changement de nom, la solution fournie par @gianpi est ce dont vous avez besoin. Sinon, vous pouvez simplement le faire this.getClass().getName()bien que vous deviez supprimer la portée statique duTAG
asgs