【问题标题】:How do you usually Tag log entries? (android)您通常如何标记日志条目? (安卓)
【发布时间】:2012-01-11 10:35:31
【问题描述】:

我想你们中的大多数人都知道 android.util.Log 所有日志记录方法都接受“字符串标记”作为第一个参数。

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

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

这看起来不太好,原因有很多:

  • 你可以告诉我这段代码没有硬编码,但它有。
  • 我的应用程序可以在不同包中具有任意数量的同名类。所以阅读日志会很困难。
  • 它不灵活。您总是将私有字段 TAG 放入您的类中。

有没有什么巧妙的方法来获得一个班级的 TAG?

【问题讨论】:

  • Android javadoc 建议使用 TAG,所以我认为这并不比在运行时获取类名更糟糕
  • 我更喜欢创建一个特定的类,比如 GeneralConstants 并将我的标签放在上面,这样我就可以访问我想要的任何类的标签; GeneralConstans.MY_TAG
  • 我认为最好在类中定义 TAG,硬编码类名很难看,但使用 proguard 的唯一可靠方法。如果您从不使用 proguard,那么 MyActivity.class.getName() 是最好的解决方案。如果您担心名称重复,只需包含包名称即可。将 TAG 名称放在不同的地方将成为维护的噩梦。

标签: android logging


【解决方案1】:

我使用了一个 TAG,但我这样初始化它:

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

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

【讨论】:

  • 我以同样的方式定义 TAG 常量。但是,我想知道,代码混淆工具将如何影响我的类名,从而影响这个常量的值?
  • 我一直手动粘贴"MyActivity.class.getName();"。我一直认为“TAG”只是 Google 等示例中的占位符……而不是实际的 Static 变量!这是一个更好的解决方案,谢谢:)
  • 为什么不删除静态并使用this.getClass().getName() 来使其更通用?
  • 您可能想尝试 this.getClass().getSimpleName() 以避免 TAG 的长度限制。如果 tag.length() > 23,则抛出 IllegalArgumentException。
  • 正如 Ralph Mueller 所说,如果您使用 Proguard(大多数 Android 项目都这样做)来混淆类名,则此技术不起作用。
【解决方案2】:

我通常会创建一个App 类,它位于不同的包中并包含有用的静态方法。其中一种方法是getTag() 方法,这样我可以在任何地方获取 TAG。
App 类看起来像这样:

编辑:改进了每个 br mob 评论(谢谢 :))

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 的行号,以便于调试。

【讨论】:

  • 我绝对不会这样做。如果你这样做,你的日志语句将会对性能造成很大影响。如果你这样做,你肯定希望 proguard 删除日志消息,而不是生产版本的警告。
  • 马特,你说得对!在生产环境中删除/剥离日志是一个好习惯 - stackoverflow.com/a/2019563/2270166
  • 这可能不再推荐,因为标签长度现在限制为 23 个字符
【解决方案3】:

转到 Android Studio -> 首选项 -> Live Templates -> AndroidLog 然后选择 Log.d(TAG, String)

模板文本替换

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

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

然后点击Edit variables并在className Name列旁边的Expression列中输入className()。

现在当你输入快捷方式logd时,它会放

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

您不再需要定义 TAG。

【讨论】:

  • 这是一个非常酷的 Android Studio 用法和一个有趣的解决问题的方法,虽然同时你实际上是在输入字符串来代替 TAG 变量,这意味着如果需要它可能会有点麻烦改变它,对吧? +1 显示功能,谢谢!
  • 我喜欢这种方式,但是我宁愿创建一个新的日志条目而不是修改现有的日志条目,以防万一它在未来的更新或其他方面发生变化。
【解决方案4】:

我喜欢改进 Yaniv 的答案 如果您有这种格式的日志 (filename.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;
}

截图:

【讨论】:

  • 爱它,“偷”它并更新我的答案:)
  • 这可能不再推荐,因为标签长度现在限制为 23 个字符
【解决方案5】:

AndroidStudio 默认有一个logt 模板(您可以输入logt 并按Tab 键将其展开为一段代码)。我建议使用它来避免从另一个类复制粘贴 TAG 定义并忘记更改您所指的类。模板默认展开为

private static final String TAG = "$CLASS_NAME$"

为避免在重构后使用旧的类名,您可以将其更改为

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

记得检查“编辑变量”按钮,并确保将 CLASS_NAME 变量定义为使用 className() 表达式并选中“如果已定义则跳过”。

【讨论】:

    【解决方案6】:

    我创建了一个名为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. 无需在 Activity 顶部定义 TAG,因为它采用类的名称。
    5. TAG 有一个前缀CCC(一个简短的、易于键入的字符串),以便在 Android Studio 的 android 监视器中仅列出您的日志。有时您正在同时运行服务或其他类。如果您必须仅按活动名称进行搜索,那么您将无法准确查看获得服务响应的时间以及您的活动发生的操作。像 CCC 这样的前缀会有所帮助,因为它会按时间顺序为您提供其发生的活动的日志

    【讨论】:

    • 很好的解决方案!我用它!但我将Context ctx 替换为Object ctx,将ctx.getClass().getName().replace(ctx.getPackageName(), "") 替换为ctx.getClass().getSimpleName()。这样,我可以在任何地方调用S.L(Object, Object)(包括在不扩展ContextFragments 中,例如)。
    【解决方案7】:

    您可以使用this.toString() 为您打印到日志的特定类获取唯一标识符。

    【讨论】:

    • 这可能会变得昂贵,具体取决于 toString() 所做的事情。
    【解决方案8】:

    当我在方法之间移动代码或重命名方法时以更新这些字符串为代价,我喜欢执行以下操作。从哲学上讲,在标签中保留“位置”或“上下文”似乎更好,而不是消息。

    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()。我发现它们更难在日志中过滤,因为要处理的内容更少。

    【讨论】:

      【解决方案9】:

      这是一个非常古老的问题,但即使是 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);
                  }
              }
          }
      }
      

      不要忘记对木材的依赖。

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

      【讨论】:

        【解决方案10】:

        如果您使用的是 Kotlin,Any 上的扩展属性可能对此有用:

        val Any.TAG: String
            get() = this::class.java.simpleName
        

        这使得TAG 可用于任何类或对象,只需要导入。

        【讨论】:

          【解决方案11】:

          对于访问此问题的用户:

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

          【讨论】:

            【解决方案12】:

            他们为 IOsched 应用程序 2019 使用 Timber 来显示调试信息:

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

            请注意,这使日志仅在调试状态下可用,并有助于您手动删除它们以在 Google Play 上启动 -

            当app在play store上发布时,我们需要从app中移除所有的Log语句,这样logcat中的用户信息、隐藏的应用数据、auth-tokens等应用数据都不能以普通的方式提供给用户文字

            查看这篇文章https://medium.com/mindorks/better-logging-in-android-using-timber-72e40cc2293d

            【讨论】:

              【解决方案13】:

              我通常使用方法名作为标记,但来自 Thread

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

              这避免了新的异常。

              【讨论】:

                【解决方案14】:
                private static final String TAG = new RuntimeException().getStackTrace()[0].getClassName();
                

                【讨论】:

                • 为什么要创建一个新的RuntimeException 只是为了获取当前的类名?非常糟糕。
                • 这就是我标记日志条目的方式,这是当我将一个类从一个项目复制到另一个项目时我可以正确重构的唯一解决方案,为什么不呢。如果您有更好、更舒适的想法,我愿意接受建议。
                • 如果您只是将 Java 类文件从一个位置复制到另一个位置,而不进行任何重命名,则需要 @gianpi 提供的解决方案。否则,您可以只执行this.getClass().getName(),尽管您必须删除TAG 的静态范围
                猜你喜欢
                • 1970-01-01
                • 2019-07-26
                • 1970-01-01
                • 2014-04-05
                • 2018-01-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-09-05
                相关资源
                最近更新 更多