Как вы обычно помечаете записи журнала? (Android)

96

Я предполагаю, что большинство из вас знает об android.util.Log. Все методы ведения журналов принимают «String tag» в качестве первого аргумента.

И мой вопрос: как вы обычно отмечаете свои журналы в своих приложениях? Я видел такой жесткий код:

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

Это выглядит не очень красиво по многим причинам:

  • Вы можете сказать мне, что у этого кода нет жесткого кода, но он есть.
  • В моем приложении могло быть любое количество классов в разных пакетах с одинаковым именем. Так что читать журнал будет сложно.
  • Это не гибко. Вы всегда добавляли в свой класс приватное поле TAG.

Есть ли какой-нибудь изящный способ получить ТЕГ для класса?

Андрей
источник
2
Использование TAG подсказывается Android Javadoc , так что я не думаю , что это хуже , чем получить имя класса во время выполнения
Vladimir
я предпочитаю создать определенный класс, например GeneralConstants, и разместить на нем свои теги, и я могу получить доступ к своим тегам любого класса, который мне нужен; GeneralConstans.MY_TAG
cagryInside
6
Я думаю, что лучше всего иметь TAG, определенный в классе, жесткое кодирование имени класса некрасиво, но это единственный надежный способ работы с proguard. Если вы никогда не используете proguard, то MyActivity.class.getName () - лучшее решение. Если вас беспокоят повторяющиеся имена, просто укажите имя пакета. Наличие имен TAG в другом месте станет кошмаром для обслуживания.
Ральф Мюллер

Ответы:

179

Я использую TAG, но инициализирую его так:

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

Таким образом, когда я реорганизую свой код, тег также изменится соответствующим образом.

Джанпи
источник
21
Таким же образом я определяю константу TAG. Однако мне интересно, как инструменты обфускации кода повлияют на имена моих классов и, как следствие, на значение этой константы?
Gumbit,
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()метод, таким образом я могу получить ТЕГ везде.
Appкласс выглядит так:

РЕДАКТИРОВАТЬ : улучшено для каждого комментария моба br (спасибо :))

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 удалял сообщения журнала для чего-либо, кроме предупреждений о производственных сборках.
Мэтт Вулф,
1
Мэтт, ты абсолютно прав! Удалять журналы на производстве - хорошая практика - stackoverflow.com/a/2019563/2270166
Янив,
2
Вероятно, это больше не рекомендуется, поскольку длина тега теперь ограничена 23 символами
Клаудио Реди
спасибо, что показали мне, как getStackTrace()работает. Но я не буду использовать его, потому что это дорого
BlueWizard
12

Перейдите в Android Studio -> Preference -> Live Templates -> AndroidLog, затем выберите Log.d (TAG, String) .

В тексте шаблона заменить

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

с участием

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

Изображение меню Android

Затем нажмите « Изменить переменные» и введите className () в столбец Expression рядом со столбцом className Name .изображение меню Android 2

Теперь, когда вы вводите ярлык, logdон помещает

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

Вам больше не нужно определять ТЕГ.

Николя Манзини
источник
1
это действительно крутое использование Android Studio и интересный подход к проблеме, хотя в то же время вы фактически вводите строку вместо переменной TAG, что означает, что это может быть немного громоздко, если необходимо ее изменить, верно? +1 за показ функциональности, спасибо!
Voy
3
Мне нравится этот способ, однако я бы предпочел создать новую запись в журнале, а не изменять существующую, просто на всякий случай, если она изменится в будущем обновлении или в чем-то еще.
Alaa
9

Мне нравится улучшать ответ Янива, если у вас есть журнал в этом формате (filename.java:XX) xx номер строки, вы можете связать ярлык таким же образом, как связывается при возникновении ошибки, таким образом я могу напрямую перейти к рассматриваемой строке просто щелкнув логарифм

Я поместил это в свое расширенное приложение, чтобы я мог использовать его в любом другом файле

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()Выражения и для нее установлен флажок «Пропустить, если определено».

Hemaolle
источник
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. Тег имеет префикс CCC(короткую, легко вводимую строку), так что в Android Studio легко отображать только ваши журналы на мониторе Android. Иногда вы запускаете службы или другие классы одновременно. Если вам нужно искать только по названию действия, вы не можете точно увидеть, когда был получен ответ службы, а затем произошло действие из вашей активности. Префикс, такой как 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().
tar
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 года предпочтительнее использовать Timber. Для правильного ведения журнала ошибки и предупреждения могут отправляться в сторонние библиотеки аварийного завершения, такие как 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

Обычно я использую имя метода в качестве тега, но из потока

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

Это позволяет избежать нового исключения.

user2705093
источник
-9
private static final String TAG = new RuntimeException().getStackTrace()[0].getClassName();
Встать
источник
3
Зачем вам создавать новый RuntimeExceptionтолько для того, чтобы получить имя текущего класса? Очень плохо.
asgs 03
Вот как я помечаю свои записи в журнале, это единственное решение, которое я могу правильно рефакторировать, копируя класс из проекта в другой, так почему бы и нет. Я открыт для предложений, если у вас есть лучшие и более удобные идеи.
Arise
1
Если вы просто копируете файлы классов Java из одного места в другое без какого-либо переименования, решение, предоставляемое @gianpi, - это то, что вам нужно. В противном случае вы могли бы просто сделать, this.getClass().getName()хотя вам придется удалить статическую область видимостиTAG
asgs