Wie markieren Sie normalerweise Protokolleinträge? (Android)

96

Ich gehe davon aus, dass die meisten von Ihnen android.util.Log kennen. Alle Protokollierungsmethoden akzeptieren 'String tag' als erstes Argument.

Und meine Frage ist: Wie kennzeichnen Sie Ihre Protokolle normalerweise in Ihren Anwendungen? Ich habe einen Hardcode wie diesen gesehen:

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

Das sieht aus vielen Gründen nicht gut aus:

  • Sie können mir sagen, dass dieser Code keinen Hardcode hat, aber er tut es.
  • Meine Anwendung kann beliebig viele Klassen in verschiedenen Paketen mit demselben Namen enthalten. Es wäre also schwierig, das Protokoll zu lesen.
  • Es ist nicht flexibel. Sie haben Ihrer Klasse immer ein privates Feld-TAG hinzugefügt.

Gibt es eine gute Möglichkeit, einen TAG für eine Klasse zu bekommen?

andrii
quelle
2
Die Verwendung von TAG wird von Android Javadoc empfohlen , daher denke ich nicht, dass es schlimmer ist, als zur Laufzeit den Klassennamen zu erhalten
Vladimir
Ich bevorzuge es, eine bestimmte Klasse wie GeneralConstants zu erstellen und meine TAGs darauf zu platzieren, und ich kann meine Tags in jeder Klasse erreichen, die ich so möchte. GeneralConstans.MY_TAG
cagryInside
6
Ich denke, es ist am besten, die TAG in der Klasse zu definieren. Die Hardcodierung des Klassennamens ist hässlich, aber die einzige zuverlässige Möglichkeit, mit Proguard zu arbeiten. Wenn Sie niemals Proguard verwenden, ist MyActivity.class.getName () die beste Lösung. Wenn Sie sich Sorgen über doppelte Namen machen, geben Sie einfach den Paketnamen an. TAG-Namen an einem anderen Ort zu haben, wird zu einem Alptraum für die Instandhaltung.
Ralph Mueller

Antworten:

179

Ich benutze einen TAG, initialisiere ihn aber folgendermaßen:

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

Auf diese Weise ändert sich auch das Tag entsprechend, wenn ich meinen Code umgestalte.

gianpi
quelle
21
Ich definiere die TAG-Konstante auf die gleiche Weise. Ich frage mich jedoch, wie sich Tools zur Verschleierung von Code auf meine Klassennamen und damit auf den Wert dieser Konstante auswirken.
Gumbit
1
Die ganze Zeit habe ich manuell eingefügt "MyActivity.class.getName();". Ich habe immer gedacht, dass "TAG" nur ein Platzhalter in Beispielen von Google usw. ist ... keine tatsächliche StaticVariable! Dies ist eine viel bessere Lösung, danke :)
wired00
4
Warum nicht die Statik entfernen und this.getClass().getName()stattdessen verwenden, um sie allgemeiner zu gestalten?
Theblang
11
Möglicherweise möchten Sie this.getClass (). GetSimpleName () ausprobieren, um die Längenbeschränkungen für TAG zu vermeiden. IllegalArgumentException wird ausgelöst, wenn tag.length ()> 23.
Michael Levy
14
Wie von Ralph Mueller erwähnt, funktioniert diese Technik nicht, wenn Sie Proguard (wie die meisten Android-Projekte) verwenden, um die Klassennamen zu verschleiern.
John Patterson
16

Normalerweise erstelle ich eine AppKlasse, die sich in einem anderen Paket befindet und nützliche statische Methoden enthält. Eine der Methoden ist eine getTag()Methode, auf diese Weise kann ich die TAG überall bekommen.
AppKlasse sieht so aus:

EDIT : Verbesserter per br Mob Kommentar (Danke :))

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

}

Und wenn ich es benutzen will:

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

Die Ausgabe der getTagMethode ist der Name der Aufruferklasse (mit dem Paketnamen) und die Zeilennummer, von der getTagaus die aufgerufen wird, um das Debuggen zu vereinfachen.

Yaniv
quelle
6
Ich würde das definitiv nicht tun. Ihre Log-Anweisungen werden einen großen Leistungseinbruch erleiden, wenn Sie dies tun. Wenn Sie dies tun, möchten Sie auf jeden Fall, dass Proguard Protokollnachrichten für weniger als Warnungen bei Produktions-Builds entfernt.
Matt Wolfe
1
Matt, du hast absolut recht! Es ist eine gute Praxis, Protokolle in der Produktion zu entfernen / zu entfernen - stackoverflow.com/a/2019563/2270166
Yaniv
2
Dies wird wahrscheinlich nicht mehr empfohlen, da die Tag-Länge jetzt auf 23 Zeichen beschränkt ist
Claudio Redi
Danke, dass du mir gezeigt hast, wie es getStackTrace()funktioniert. Aber ich werde es nicht benutzen, weil es teuer ist
BlueWizard
12

Gehen Sie zu Android Studio -> Einstellungen -> Live-Vorlagen -> AndroidLog und wählen Sie Log.d (TAG, String) .

Im Vorlagentext ersetzen

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

mit

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

Bild des Android-Menüs

Klicken Sie dann auf Variablen bearbeiten und geben Sie className () in die Spalte Ausdruck neben der Spalte className Name ein .Bild von Android-Menü 2

Wenn Sie nun die Verknüpfung eingeben logd, wird sie eingefügt

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

Sie müssen kein TAG mehr definieren.

Nicolas Manzini
quelle
1
Das ist eine wirklich coole Verwendung von Android Studio und eine interessante Herangehensweise an das Problem, obwohl Sie gleichzeitig tatsächlich eine Zeichenfolge anstelle der TAG-Variablen eingeben, was bedeutet, dass es etwas umständlich sein kann, wenn es geändert werden muss, oder? +1 für das Zeigen der Funktionalität, danke!
Voy
3
Ich mag diese Art und Weise, aber ich möchte lieber einen neuen Protokolleintrag erstellen, anstatt den vorhandenen zu ändern, um auf der sicheren Seite zu sein, wenn er sich in einem zukünftigen Update oder etwas anderem ändert.
Alaa
9

Ich möchte die Antwort von Yaniv verbessern, wenn Sie das Protokoll in diesem Format haben (Dateiname.java:XX) Klicken Sie einfach auf den Logcat

Ich habe dies in meine erweiterte Anwendung eingefügt, damit ich es in jeder anderen Datei verwenden kann

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

Bildschirmfoto:

br mob
quelle
Ich liebe es, "stehle" es und aktualisiere meine Antwort :)
Yaniv
4
Dies wird wahrscheinlich nicht mehr empfohlen, da die Tag-Länge jetzt auf 23 Zeichen beschränkt ist
Claudio Redi
3

AndroidStudio verfügt logtstandardmäßig über eine Vorlage (Sie können die logtTabulatortaste eingeben und drücken, um sie zu einem einzelnen Code zu erweitern). Ich empfehle, dies zu verwenden, um zu vermeiden, dass die TAG-Definition aus einer anderen Klasse kopiert und vergessen wird, die Klasse zu ändern, auf die Sie sich beziehen. Die Vorlage wird standardmäßig auf erweitert

private static final String TAG = "$CLASS_NAME$"

Um zu vermeiden, dass der alte Klassenname nach dem Refactoring verwendet wird, können Sie dies in ändern

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

Denken Sie daran, die Schaltfläche "Variablen bearbeiten" zu aktivieren und sicherzustellen, dass die CLASS_NAMEVariable für die Verwendung des className()Ausdrucks definiert ist und "Überspringen, wenn definiert" aktiviert ist.

Hemaolle
quelle
2

Ich habe eine Klasse statischer Variablen, Methoden und Klassen mit dem Namen erstellt S.

Das Folgende ist die Protokollierungsmethode:

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

Es wird in jeder Klasse aufgerufen, da S.L(this, whaterver_object);The getClass().getName()auch den Paketnamen anfügt. Daher entferne ich ihn, um zu vermeiden, dass das Tag unnötig lang wird.

Vorteile:

  1. Kürzer als Log.d(TAG,
  2. Int-Werte müssen nicht in ihre Zeichenfolge konvertiert werden. Infact keine Notwendigkeit zu tippentoString
  3. Vergessen Sie nicht, Log.djemals zu löschen , da ich nur die Methode löschen muss und die Speicherorte aller Protokolle rot markiert werden.
  4. TAG muss nicht oben in der Aktivität definiert werden, da es den Namen der Klasse trägt.
  5. Das TAG hat das Präfix CCC(eine kurze, einfach einzugebende Zeichenfolge), sodass es einfach ist, nur Ihre Protokolle im Android Monitor in Android Studio aufzulisten. Manchmal führen Sie Dienste oder andere Klassen gleichzeitig aus. Wenn Sie nur nach dem Aktivitätsnamen suchen müssen, können Sie nicht genau sehen, wann eine Serviceantwort erhalten wurde und dann eine Aktion aus Ihrer Aktivität ausgeführt wurde. Ein Präfix wie CCC hilft, da es Ihnen Protokolle chronologisch mit der Aktivität gibt, in der es aufgetreten ist
Suku
quelle
1
Tolle Lösung! Ich benutze es! Aber ich habe Context ctxnach Object ctxund ctx.getClass().getName().replace(ctx.getPackageName(), "")nach ersetzt ctx.getClass().getSimpleName(). Auf diese Weise kann ich S.L(Object, Object)überall anrufen (auch in Fragments, die nicht Contextsofort erweitert werden).
Antonio Vinicius Menezes Medei
1

Sie können this.toString()eine eindeutige Kennung für die bestimmte Klasse abrufen, in der Sie im Protokoll drucken.

kaspermoerch
quelle
Dies kann je nach dem, was es toString()tut , teuer werden .
Teer
1

Auf Kosten der Aktualisierung dieser Zeichenfolgen beim Verschieben von Code zwischen Methoden oder beim Umbenennen von Methoden möchte ich Folgendes tun. Philosophisch scheint es auch besser zu sein, "Ort" oder "Kontext" im Tag zu behalten, nicht die Nachricht.

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

}

Der Vorteil hierbei ist, dass Sie eine einzelne Methode herausfiltern können, auch wenn der Inhalt nicht statisch ist, z

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

Der einzige Nachteil ist, dass ich beim Umbenennen f()in g()diese Zeichenfolge berücksichtigen muss. Auch das automatische IDE-Refactoring fängt diese nicht ab.

Ich meine, ich war eine Weile ein Fan von der Verwendung des Kurzklassennamens LOG_TAG = MyClass.class.getSimpleName(). Ich fand es schwieriger, sie in den Protokollen zu filtern, weil weniger zu tun war.

Teer
quelle
1

Es ist eine sehr alte Frage, aber obwohl eine aktualisierte Antwort für Juli 2018 vorliegt, ist es vorzuziehen, Holz zu verwenden. Um die korrekte Protokollierung zu protokollieren, können Fehler und Warnungen an Absturzbibliotheken von Drittanbietern wie Firebase oder Crashlytics gesendet werden.

In der Klasse, die Application implementiert , sollten Sie Folgendes hinzufügen:

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

Vergessen Sie nicht die Holzabhängigkeit.

implementation 'com.jakewharton.timber:timber:4.7.1'
aleksandrbel
quelle
0

Für diejenigen Benutzer, die diese Frage besuchen:

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

Sie verwenden Timber für die IOsched-App 2019, um Debug-Informationen anzuzeigen:

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

Verwendung

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

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

Beachten Sie, dass dadurch die Protokolle nur während des DEBUG-Status verfügbar sind und Sie sie manuell für den Start in Google Play entfernen können.

Wenn Sie die App im Play Store freigeben, müssen Sie alle Log-Anweisungen aus der App entfernen, damit dem Benutzer in logcat keine der Anwendungsdaten wie Benutzerinformationen, versteckte Anwendungsdaten und Authentifizierungstoken als einfacher Text zur Verfügung stehen

Lesen Sie diesen Artikel unter https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d

Dan Alboteanu
quelle
-2

Normalerweise verwende ich den Methodennamen als Tag, aber von Thread

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

Dies vermeidet die neue Ausnahme.

user2705093
quelle
-9
private static final String TAG = new RuntimeException().getStackTrace()[0].getClassName();
Entstehen
quelle
3
Warum sollten Sie eine neue erstellen RuntimeException, um den aktuellen Klassennamen zu erhalten? Sehr schlecht.
Asgs
Auf diese Weise TAG ich meine Protokolleinträge. Dies ist die einzige Lösung, die ich ordnungsgemäß umgestalten kann, wenn ich eine Klasse von einem Projekt in ein anderes kopiere. Warum also nicht? Ich bin offen für Vorschläge, wenn Sie bessere und bequemere Ideen haben.
Stehen Sie am
1
Wenn Sie nur Java-Klassendateien ohne Umbenennung von einem Speicherort an einen anderen kopieren, ist die von @gianpi bereitgestellte Lösung erforderlich. Andernfalls könnten Sie einfach tun, this.getClass().getName()obwohl Sie den statischen Bereich derTAG
asgs