您通常如何标记日志条目?(安卓)


96

我假设大多数人都知道android.util.Log所有日志记录方法都将“字符串标记”作为第一个参数。

我的问题是您通常如何在应用程序中标记日志? 我看过这样的一些硬代码:

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

由于许多原因,这看起来不太好:

  • 您可以告诉我此代码没有硬代码,但是有。
  • 我的应用程序可以在具有相同名称的不同包中包含任意数量的类。因此,很难读取日志。
  • 它不灵活。您始终在班级中放置了一个私有字段TAG。

有什么整洁的方法可以获取课程的TAG?


2
Android Javadoc建议使用TAG ,因此我认为它比在运行时获取类名称更糟
Vladimir

我更喜欢创建一个像GeneralConstants这样的特定类,并在上面放上我的标签,这样我就可以到达我想要的任何类的标签;GeneralConstans.MY_TAG
cagry11年

6
我认为最好在类中定义TAG,对类名进行硬编码很难,但这是与proguard一起使用的唯一可靠方法。如果您从不使用proguard,那么MyActivity.class.getName()是最佳解决方案。如果您担心重复的名称,只需添加软件包名称即可。将TAG名称放在其他位置将成为维护的噩梦。
拉尔夫·穆勒

Answers:


179

我使用TAG,但是我将其初始化为:

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

这样,当我重构代码时,标签也会相应更改。


21
我以相同的方式定义TAG常数。但是,我想知道,代码混淆工具将如何影响我的类名,从而影响此常量的值?
2012年

1
这一次我一直手工粘贴"MyActivity.class.getName();"。我一直认为“ TAG”只是Google等示例中的占位符,而不是实际Static变量!这是一个更好的解决方案,谢谢:)
13年

4
为什么不删除静态函数,this.getClass().getName()而是使用它来使其更通用?
theblang 2014年

11
您可能需要尝试this.getClass()。getSimpleName()来避免TAG的长度限制。如果tag.length()> 23,则会引发IllegalArgumentException。–
Michael Levy

14
正如Ralph Mueller所提到的那样,如果您使用Proguard(就像大多数Android项目一样)来混淆类名,则此技术将不起作用。
约翰·帕特森

16

我通常创建一个App类,该类位于其他程序包中,并包含有用的静态方法。一种方法是一种getTag()方法,通过这种方法我可以在任何地方获得TAG。
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
Yaniv 2015年

2
由于标签长度现在限制为23个字符,因此可能不再建议这样做
Claudio Redi 2015年

感谢您向我展示getStackTrace()工作原理。但是我不会使用它,因为它很昂贵
BlueWizard 2016年

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 名称列旁边的“ 表达式”列中输入className()。Android菜单2的图片

现在,当您键入快捷方式时logd,它将放入

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

您不再需要定义TAG。


1
这确实是Android Studio的一个很酷的用法,并且是解决问题的一种有趣的方法,尽管与此同时,您实际上是在输入字符串来代替TAG变量,这意味着如果需要更改它可能会有些麻烦,对吧?+1表示功能,谢谢!
Voy

3
我喜欢这种方式,但是我宁愿创建一个新的日志条目,而不是修改现有的日志条目,只是为了在将来的更新中更改它时保持安全。
Alaa

9

如果您具有这种格式的日志(filename.java:XX)xx行号,我可以改善Yaniv的答案,您可以链接快捷方式,就像发生错误时链接链接的方式一样,这样我就可以直接转到有问题的行只需单击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;
}

屏幕截图:


爱它,“偷”它并更新我的答案:)
Yaniv

4
这可能是不再推荐,因为标签长度现在限制为23个字符
克劳迪奥·雷迪

3

AndroidStudio logt默认情况下有一个模板(您可以键入logt并按tab使其扩展为代码片段)。我建议使用此方法以避免复制粘贴另一个类的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);并且getClass().getName()还会附加包名称,因此,我将其删除以避免避免不必要地延长标签的长度。

优点:

  1. 比。。。短 Log.d(TAG,
  2. 无需将int值转换为其字符串。实际上不需要输入toString
  3. 别忘了删除Log.d,因为我只需要删除方法,所有日志的位置都标记为红色。
  4. 无需在活动顶部定义TAG,因为它采用了类的名称。
  5. TAG的前缀为CCC(简短且易于输入的字符串),因此很容易仅列出Android Studio中android监视器中的日志。有时您同时运行服务或其他类。如果您必须仅按活动名称进行搜索,那么您将无法确切看到何时获得服务响应,然后发生了来自活动的操作。像CCC这样的前缀可以帮助您按时间顺序记录发生它的活动

1
很好的解决方案!我用它!但是我换成Context ctx通过Object ctxctx.getClass().getName().replace(ctx.getPackageName(), "")通过ctx.getClass().getSimpleName()。这样,我可以S.L(Object, Object)在任何地方调用(包括in中Fragment没有扩展的Context,以进行实例化)。
Antonio Vinicius Menezes Medei

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年7月有更新的答案,使用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);
            }
        }
    }
}

不要忘记Timber依赖性。

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


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上启动-

在Play商店上发布应用程序时,我们需要从应用程序中删除所有Log语句,以使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只是为了获得当前的班级名称?很坏。
2013年

这就是我标记日志条目的方式,这是将类从项目复制到另一个项目时可以正确重构的唯一解决方案,所以为什么不这样做。如果您有更好,更舒适的想法,我欢迎您提出建议。
2013年

1
如果您只是将Java类文件从一个位置复制到另一位置,而不进行任何重命名,则需要@gianpi提供的解决方案。否则,您可以这样做,this.getClass().getName()尽管您必须删除TAG
ass
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.