Як зазвичай ви позначаєте записи в журналі? (android)


96

Я припускаю, що більшість з вас знають про android.util.Log. Усі методи ведення журналу приймають "тег рядка" як перший аргумент.

І моє запитання: Як ви зазвичай позначаєте свої журнали у своїх програмах? Я бачив такий хардкод, як цей:

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

Це не виглядає приємно з багатьох причин:

  • Ви можете сказати мені, що в цьому коді немає жорсткого коду, але він є.
  • У моїй програмі може бути будь-яка кількість класів у різних пакетах з однаковим іменем. Тож читати журнал було б важко.
  • Це не гнучко. Ви завжди вкладали приватний тег TAG у свій клас.

Чи є якийсь акуратний спосіб отримати ТЕГ для класу?


2
Використання TAG пропонується Android javadoc , тому я не думаю, що це гірше, ніж отримання назви класу під час виконання
Володимир

я вважаю за краще створити конкретний клас, такий як GeneralConstants, і розміщувати на ньому свої теги, і я можу переходити до моїх тегів до будь-якого класу, який мені подобається; GeneralConstans.MY_TAG
cagryInside

6
Я думаю, що найкраще визначити TAG у класі, жорстке кодування назви класу є потворним, але єдиним надійним способом роботи з proguard. Якщо ви ніколи не використовуєте proguard, тоді MyActivity.class.getName () - найкраще рішення. Якщо вас турбують дублікати імен, просто вкажіть ім'я пакета. Розміщення імен TAG в іншому місці стане кошмаром для обслуговування.
Ральф Мюллер

Відповіді:


179

Я використовую тег, але ініціюю його так:

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

Таким чином, коли я рефакторирую свій код, тег також буде відповідно змінюватися.


21
Я визначаю константу TAG таким же чином. Однак мені цікаво, як інструменти затухання коду вплинуть на імена моїх класів і як результат на значення цієї константи?
Гамбіт

1
весь цей час я вручну вставляв "MyActivity.class.getName();". Я завжди думав, що "TAG" - це просто заповнювач у прикладах від Google тощо ... а не фактична Staticзмінна! Це набагато краще рішення, дякую :)
wired00

4
Чому б не видалити статику та не використовувати її, this.getClass().getName()щоб зробити її більш загальною?
theblang

11
Можливо, ви захочете спробувати this.getClass (). GetSimpleName (), щоб уникнути обмежень довжини на TAG. IllegalArgumentException кидається, якщо tag.length ()> 23.
Майкл Леві

14
Як згадував Ральф Мюллер, цей прийом не працює, якщо ви використовуєте Proguard (як це робить більшість проектів Android) для затухання імен класів.
Джон Паттерсон,

16

Зазвичай я створюю Appклас, який знаходиться в іншому пакеті і містить корисні статичні методи. Одним із методів є getTag()метод, таким чином я можу отримати TAG скрізь.
Appклас виглядає так:

РЕДАГУВАТИ : Покращено коментар на кожний моб (спасибі :))

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

}

І коли я хочу цим скористатися:

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

Результатом getTagметоду є ім'я класу абонента (з ім'ям пакета) та номер рядка, з getTagякого викликається, для зручного налагодження.


6
Я точно не став би цього робити. Якщо ви це зробите, ваші оператори журналу отримають великий удар. Якщо ви зробите це, ви точно захочете мати proguard видалити повідомлення журналу для чогось меншого, ніж попередження про виробничі збірки.
Matt Wolfe

1
Метт, ти абсолютно правий! Це хороша практика видаляти / видаляти журнали на виробництві - stackoverflow.com/a/2019563/2270166
Янів

2
Це, мабуть, більше не рекомендується, оскільки довжина тегу тепер обмежена 23 символами
Клаудіо Реді,

дякую, що показали мені, як це getStackTrace()працює. Але я не буду ним користуватися, бо це дорого
BlueWizard

12

Перейдіть до Android Studio -> налаштування -> Шаблони в реальному часі -> AndroidLog, а потім виберіть Log.d (TAG, String) .

У текстовому шаблоні замінити

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

з

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

Зображення меню Android

Потім натисніть Редагувати змінні та введіть className () у стовпець Вираз поруч із стовпцем Ім'я класу .зображення меню Android 2

Тепер, коли ви введете ярлик, який logdвін поставить

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

Вам більше не потрібно визначати TAG.


1
це справді круте використання Android Studio та цікавий підхід до проблеми, хоча в той же час ви фактично вводите рядок замість змінної TAG, тобто це може бути трохи громіздким, якщо потрібно змінити його, так? +1 за те, що ви показали функціональність, дякую!
Вой

3
Мені подобається цей спосіб, однак я волів би створити новий запис журналу, замість того, щоб модифікувати існуючий, просто щоб бути в безпеці, якщо він зміниться в майбутньому оновленні чи щось інше.
Алаа

9

Мені подобається покращити відповідь Yaniv, якщо у вас є журнал у цьому форматі (ім'я файлу.java:XX) номер рядка xx, ви можете зв'язати ярлик так само, як прив'язується при помилці, таким чином я можу перейти безпосередньо до відповідного рядка просто клацніть на logcat

Я помістив це всередину свого розширеного додатка, щоб я міг використовувати його в кожному іншому файлі

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

Знімок екрана:


Любити це, «красти» і оновлювати свою відповідь :)
Янів,

4
Це, мабуть, більше не рекомендується, оскільки довжина тегу тепер обмежена 23 символами
Клаудіо Реді,

3

AndroidStudio має logtшаблон за замовчуванням (ви можете ввести logtта натиснути вкладку, щоб розгорнути його до коду коду). Я рекомендую використовувати це, щоб уникнути копіювання вставки визначення TAG з іншого класу та забути змінити клас, до якого ви маєте на увазі. Шаблон за замовчуванням розширюється до

private static final String TAG = "$CLASS_NAME$"

Щоб уникнути використання старого імені класу після рефакторингу, ви можете змінити це на

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

Не забудьте перевірити кнопку "Редагувати змінні" та переконатися, що CLASS_NAMEзмінна визначена для використання className()виразу та має галочку "Пропустити, якщо визначено".


2

Я створив клас статичних змінних, методів та класів, названих як S.

Нижче наведено метод реєстрації:

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

Він викликається в будь-якому класі, оскільки S.L(this, whaterver_object);The getClass().getName()також додає ім'я пакета, отже, я видаляю його, щоб уникнути надмірного збільшення тегу.

Переваги:

  1. Коротше ніж Log.d(TAG,
  2. Не потрібно перетворювати значення int у їх рядок. Факт не потрібно вводитиtoString
  3. Log.dНіколи не забуду видалити, оскільки мені просто потрібно видалити метод, а розташування всіх журналів позначено червоним.
  4. Не потрібно визначати TAG у верхній частині дії, оскільки воно бере назву класу.
  5. TAG має префікс CCC(короткий, простий для введення рядок), так що легко перерахувати лише свої журнали на моніторі Android в Android Studio. Іноді ви одночасно запускаєте служби або інші класи. Якщо вам доводиться шукати лише за назвою активності, ви не можете точно побачити, коли була отримана відповідь служби, і тоді відбулася дія від вашої активності. Префікс, такий як CCC, допомагає, оскільки дає вам журнали хронологічно з діяльністю, в якій він відбувся

1
Чудове рішення! Я цим користуюся! Але я замінив Context ctxна Object ctxі ctx.getClass().getName().replace(ctx.getPackageName(), "")на ctx.getClass().getSimpleName(). Таким чином, я можу зателефонувати S.L(Object, Object)куди завгодно (у тому числі в Fragments, які не поширюються Context, на мить).
Антоніо Вініцій Менезес Медей

1

Ви можете використати, this.toString()щоб отримати унікальний ідентифікатор для конкретного класу, в якому ви друкуєте в журналі.


Це може стати дорогим залежно від того, що toString()робить.
смола

1

За рахунок оновлення цих рядків, коли я переміщую код між методами або перейменовую методи, мені подобається робити наступне. Філософським чином, здається, також краще зберігати "тег" або "контекст" у тезі, а не в повідомленні.

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

}

Перевага тут полягає в тому, що ви можете відфільтрувати один метод, навіть якщо вміст не є статичним, наприклад

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

Єдиним недоліком є ​​те, що коли я перейменовуюсь f()на, g()мені потрібно пам’ятати про цей рядок. Крім того, автоматичний рефакторинг IDE їх не схопить.

Я деякий час я любив використовувати коротку назву класу, я маю на увазі LOG_TAG = MyClass.class.getSimpleName(). Мені було важче їх відфільтрувати в журналах, тому що продовжувати було менше.


1

Це дуже давнє запитання, але навіть думаючи, що оновлена ​​відповідь на липень 2018 року, краще використовувати деревину. Для того, щоб правильно реєструвати журнали, помилки та попередження можна надсилати до сторонніх бібліотек збоїв, таких як Firebase або Crashlytics.

У класі, який реалізує Application , слід додати це:

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

Не забувайте про залежність від деревини.

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

0

Для тих користувачів, які відвідують це питання:

private val TAG:String = this.javaClass.simpleName;

0

вони використовують Timber для програми IOsched 2019, щоб показати інформацію про налагодження:

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

використання

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

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

зауважте, що це робить журнали доступними лише під час DEBUG і полегшує вам завдання видалення їх вручну для запуску в Google Play -

Коли випускаєте програму в магазині відтворення, нам потрібно видалити всі оператори журналу з програми, щоб жодна інформація про додаток, така як інформація користувача, приховані дані програми, маркери автентифікації, не була доступна користувачеві в logcat як звичайний текст

перегляньте цю статтю https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d


-2

Зазвичай я використовую назву методу як тег, але від Thread

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

Це дозволяє уникнути нового винятку.


-9
private static final String TAG = new RuntimeException().getStackTrace()[0].getClassName();

3
Чому б вам створити новий, RuntimeExceptionлише щоб отримати поточну назву класу? Дуже погано.
asgs

Ось як я позначаю свої записи в журналі, це єдине рішення, яке я можу правильно рефакторировать, коли копіюю клас з проекту в інший, то чому б і ні. Я відкритий для пропозицій, якщо у вас є кращі та зручніші ідеї.
Вихід

1
Якщо ви просто копіюєте файли класу Java з одного місця в інше, без будь-якого перейменування, потрібне рішення, надане @gianpi. В іншому випадку ви можете просто зробити, this.getClass().getName()хоча вам доведеться видалити статичний обсягTAG
asgs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.